From 6de97e4b42b300a0152d07d217ac81e653861e15 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Tue, 8 Sep 2026 15:22:32 +0200 Subject: [PATCH 1/3] drivers/flash: drop dead code Remove flash driver code that nothing builds or calls: - py25q128ha.c and the py25q128ha/n25q flash region headers are not referenced by any CMake or Kconfig file. - flash_sleep_when_idle() and flash_get_sleep_when_idle() are hardcoded no-ops, flash_enable_write_protection() is empty on every part and flash_switch_mode() is unimplemented ("NYI") everywhere. - flash_whoami(), flash_get_size(), flash_is_whoami_correct(), debug_flash_dump_registers(), flash_use()/flash_release*() and flash_erase_bulk() have no callers (the latter was already #if 0). The "flash switchmode" and commented-out "format flash" prompt commands go with them. Co-Authored-By: Claude Fable 5.1 Signed-off-by: Gerard Marull-Paretas --- include/pbl/drivers/flash.h | 69 ------- src/fw/console/prompt_commands.c | 9 - src/fw/console/prompt_commands.h | 4 - src/fw/drivers/flash/flash_api.c | 45 +---- src/fw/drivers/flash/py25q128ha.c | 182 ------------------ src/fw/flash_region/filesystem_regions_n25q.h | 28 --- src/fw/flash_region/flash_region_n25q.h | 70 ------- src/fw/flash_region/flash_region_py25q128ha.h | 83 -------- src/fw/main.c | 2 - src/fw/services/prf_update/service.c | 4 - 10 files changed, 2 insertions(+), 494 deletions(-) delete mode 100644 src/fw/drivers/flash/py25q128ha.c delete mode 100644 src/fw/flash_region/filesystem_regions_n25q.h delete mode 100644 src/fw/flash_region/flash_region_n25q.h delete mode 100644 src/fw/flash_region/flash_region_py25q128ha.h diff --git a/include/pbl/drivers/flash.h b/include/pbl/drivers/flash.h index 073e09f244..1745ae6c92 100644 --- a/include/pbl/drivers/flash.h +++ b/include/pbl/drivers/flash.h @@ -9,9 +9,6 @@ #include "system/status_codes.h" -static const uint32_t EXPECTED_SPI_FLASH_ID_32MBIT = 0x20bb16; -static const uint32_t EXPECTED_SPI_FLASH_ID_64MBIT = 0x20bb17; - typedef struct FlashSecurityRegisters { const uint32_t *sec_regs; uint8_t num_sec_regs; @@ -27,12 +24,6 @@ void flash_init(void); //! Stop all flash transactions. void flash_stop(void); -/** - * Retrieve the first 3 bytes of the flash's device id. This ID - * should remain fixed across all chips. - */ -uint32_t flash_whoami(void); - /** * Read 1 or more bytes starting at the specified 24bit address into * the provided buffer. This function does no range checking, so it is @@ -105,14 +96,6 @@ bool flash_sector_is_erased(uint32_t sector_addr); */ bool flash_subsector_is_erased(uint32_t sector_addr); -/** - * Erase the entire contents of flash. - * - * Note: This is a very slow (up to a minute) blocking operation. Don't let the watchdog kill - * you when calling this. - */ -void flash_erase_bulk(void); - /** * Erase a region of flash asynchronously using as few erase operations as * possible. @@ -125,27 +108,6 @@ void flash_erase_optimal_range( uint32_t min_start, uint32_t max_start, uint32_t min_end, uint32_t max_end, FlashOperationCompleteCb on_complete, void *context); -/** - * Configure the flash driver to enter a deep sleep mode between commands. - */ -void flash_sleep_when_idle(bool enable); - -//! @return True if sleeping when idle is currently enabled. -bool flash_get_sleep_when_idle(void); - -void debug_flash_dump_registers(void); - -//! @return true if the flash peripheral has been initialized. -bool flash_is_initialized(void); - -//! Helper function to check that the Flash ID (whoami) is correct -//! @return true if the flash ID matches what we expect based on the board config -bool flash_is_whoami_correct(void); - -//! Helper function to extract the Flash Size from the ID (whoami) -//! @return the size of the flash in bytes -size_t flash_get_size(void); - // This is only intended to be called when entering stop mode. It does not use // any locks because IRQs have already been disabled. The idea is to only incur // the wait penalty for entering/exiting deep sleep mode for the flash @@ -155,29 +117,12 @@ size_t flash_get_size(void); void flash_power_down_for_stop_mode(void); void flash_power_up_after_stop_mode(void); -typedef enum { - FLASH_MODE_ASYNC = 0, - FLASH_MODE_SYNC_BURST, - - // Add new modes above this - FLASH_MODE_NUM_MODES -}FlashModeType; - -/** - * Manually switches modes between asynchronous/synchronous - * - */ -void flash_switch_mode(FlashModeType mode); - // Returns the sector address that the given flash address lies in uint32_t flash_get_sector_base_address(uint32_t flash_addr); // Returns the subsector address that the given flash address lies in uint32_t flash_get_subsector_base_address(uint32_t flash_addr); -// Enable write protection on flash -void flash_enable_write_protection(void); - // Write-protects the prf region of flash void flash_prf_set_protection(bool do_protect); @@ -188,20 +133,6 @@ uint32_t flash_crc32(uint32_t flash_addr, uint32_t length); uint32_t flash_calculate_legacy_defective_checksum(uint32_t flash_addr, uint32_t length); -//! Call this before any external flash access (including memory-mapped) -//! to power on the flash peripheral if it wasn't already, and -//! to increase the internal reference counter that prevents flash peripheral from powering down. -void flash_use(void); - -//! Convenience for \ref flash_release_many with num_locks = 1 -void flash_release(void); - -//! Call this after you finished accessing external flash -//! to decrease the internal reference counter by num_locks, and -//! to turn off the flash peripheral if the reference counter reaches 0 -//! param num_locks usually 1, the amount by which the reference counter should be decremented -void flash_release_many(uint32_t num_locks); - //! Read security register //! //! @param addr The address of the security register to read. diff --git a/src/fw/console/prompt_commands.c b/src/fw/console/prompt_commands.c index ddcf6fbb0e..e4e63f86d3 100644 --- a/src/fw/console/prompt_commands.c +++ b/src/fw/console/prompt_commands.c @@ -72,10 +72,6 @@ extern void command_read_word(const char* address_str) { prompt_send_response_fmt(buffer, sizeof(buffer), "0x%"PRIx32" = 0x%"PRIx32, address, word); } -void command_format_flash(void) { - flash_erase_bulk(); -} - void command_erase_flash(const char *address_str, const char *length_str) { int32_t address = str_to_address(address_str); if (address < 0) { @@ -196,11 +192,6 @@ void command_flash_read(const char* address_str, const char* length_str) { kernel_free(buffer); } -void command_flash_switch_mode (const char* mode_str) { - int mode = atoi(mode_str); - flash_switch_mode(mode); -} - #define WRITE_PAGE_SIZE_BYTES 64 void command_flash_fill (const char* address_str, const char* length_str, const char* value_str) { int32_t address = str_to_address(address_str); diff --git a/src/fw/console/prompt_commands.h b/src/fw/console/prompt_commands.h index 20c05a55c0..ce2fcfdbe6 100644 --- a/src/fw/console/prompt_commands.h +++ b/src/fw/console/prompt_commands.h @@ -76,10 +76,8 @@ extern void command_accel_softreset(void); extern void command_dump_flash(const char*, const char*); extern void command_crc_flash(const char*, const char*); -extern void command_format_flash(void); extern void command_erase_flash(const char*, const char*); extern void command_flash_read(const char*, const char*); -extern void command_flash_switch_mode(const char*); extern void command_flash_fill(const char*, const char*, const char*); extern void command_flash_test_locked_sectors(void); extern void command_flash_stress(const char *); @@ -407,7 +405,6 @@ static const Command s_prompt_commands[] = { { "als read", command_als_read, 0}, { "flash read", command_flash_read, 2}, - { "flash switchmode", command_flash_switch_mode, 1}, { "flash fill", command_flash_fill, 3}, { "flash validate", command_flash_validate, 0}, { "flash erased_sectors", command_flash_show_erased_sectors, 1}, @@ -519,7 +516,6 @@ static const Command s_prompt_commands[] = { // Flash manipulation commands { "dump flash", command_dump_flash, 2 }, - // { "format flash", command_format_flash, 0 }, { "flash unprotect", command_flash_unprotect, 0 }, diff --git a/src/fw/drivers/flash/flash_api.c b/src/fw/drivers/flash/flash_api.c index b260870ad3..822fa0958b 100644 --- a/src/fw/drivers/flash/flash_api.c +++ b/src/fw/drivers/flash/flash_api.c @@ -369,10 +369,6 @@ void flash_erase_subsector_blocking(uint32_t subsector_addr) { prv_flash_erase_blocking(subsector_addr, true /* is_subsector */); } -void flash_enable_write_protection(void) { - flash_impl_enable_write_protection(); -} - void flash_prf_set_protection(bool do_protect) { status_t status; pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); @@ -387,31 +383,12 @@ void flash_prf_set_protection(bool do_protect) { pbl_mutex_unlock(&s_flash_lock); } -#if 0 -void flash_erase_bulk(void) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - flash_impl_erase_bulk_begin(); - while (flash_impl_erase_is_in_progress()) { - psleep(10); - } - pbl_mutex_unlock(&s_flash_lock); -} -#endif - -void flash_sleep_when_idle(bool enable) { - // the S29VS flash automatically enters and exits standby -} - -bool flash_get_sleep_when_idle(void) { - return false; -} - -bool flash_is_initialized(void) { +static bool prv_is_initialized(void) { return s_flash_initialized; } void flash_stop(void) { - if (!flash_is_initialized()) { + if (!prv_is_initialized()) { // Not yet initialized, nothing to do. return; } @@ -427,12 +404,6 @@ void flash_stop(void) { } } -void flash_switch_mode(FlashModeType mode) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - flash_impl_set_burst_mode(mode == FLASH_MODE_SYNC_BURST); - pbl_mutex_unlock(&s_flash_lock); -} - uint32_t flash_get_sector_base_address(uint32_t flash_addr) { return flash_impl_get_sector_base_address(flash_addr); } @@ -457,18 +428,6 @@ bool flash_subsector_is_erased(uint32_t sector_addr) { return flash_impl_blank_check_subsector(flash_impl_get_subsector_base_address(sector_addr)); } -void flash_use(void) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - flash_impl_use(); - pbl_mutex_unlock(&s_flash_lock); -} - -void flash_release_many(uint32_t num_locks) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - flash_impl_release_many(num_locks); - pbl_mutex_unlock(&s_flash_lock); -} - status_t flash_read_security_register(uint32_t addr, uint8_t *val) { status_t status; diff --git a/src/fw/drivers/flash/py25q128ha.c b/src/fw/drivers/flash/py25q128ha.c deleted file mode 100644 index b708b8a02a..0000000000 --- a/src/fw/drivers/flash/py25q128ha.c +++ /dev/null @@ -1,182 +0,0 @@ -/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include "board/board.h" -#include -#include -#include -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "pbl/util/size.h" - -static const uint32_t prv_sec_regs[] = { - 0x00001000, - 0x00002000, - 0x00003000, -}; - -static QSPIFlashPart QSPI_FLASH_PART = { - .instructions = - { - .fast_read = 0x0B, - .read2o = 0x3B, - .read2io = 0xBB, - .read4o = 0x6B, - .read4io = 0xEB, - .pp = 0x02, - .pp4o = 0x32, - .erase_sector_4k = 0x20, - .erase_block_64k = 0xD8, - .write_enable = 0x06, - .write_disable = 0x04, - .rdsr1 = 0x05, - .rdsr2 = 0x35, - .wrsr = 0x01, - .erase_suspend = 0x75, - .erase_resume = 0x7A, - .enter_low_power = 0xB9, - .exit_low_power = 0xAB, - .enter_quad_mode = 0x38, - .reset_enable = 0x66, - .reset = 0x99, - .qspi_id = 0x9F, - .en4b = 0xB7, - .erase_sec = 0x44, - .program_sec = 0x42, - .read_sec = 0x48, - }, - .status_bit_masks = - { - .busy = 1 << 0, - .write_enable = 1 << 1, - }, - .flag_status_bit_masks = - { - .sec_lock = (1 << 5) | (1 << 4) | (1 << 3), - .erase_suspend = 1 << 7, - }, - .dummy_cycles = - { - .fast_read = 4, - }, - .sec_registers = { - .sec_regs = prv_sec_regs, - .num_sec_regs = ARRAY_LENGTH(prv_sec_regs), - .sec_reg_size = 1024, - }, - .supports_block_lock = false, - .reset_latency_ms = 12, - .suspend_to_read_latency_us = 20, - .standby_to_low_power_latency_us = 3, - .low_power_to_standby_latency_us = 20, - .supports_fast_read_ddr = false, - .qer_type = JESD216_DW15_QER_S2B1v1, - .qspi_id_value = 0x852018, - .size = 0x1000000, /* 16MB */ - .name = "PY25Q128HA", -}; - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - return (addr & SECTOR_ADDR_MASK); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return (addr & SUBSECTOR_ADDR_MASK); -} - -void flash_impl_enable_write_protection(void) {} - -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { - return S_SUCCESS; -} - -status_t flash_impl_init(bool coredump_mode) { - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, coredump_mode); - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { return qspi_flash_is_erase_complete(QSPI_FLASH); } - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, subsector_addr, true /* is_subsector */); -} -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, sector_addr, false /* !is_subsector */); -} - -status_t flash_impl_erase_suspend(FlashAddress sector_addr) { - return qspi_flash_erase_suspend(QSPI_FLASH, sector_addr); -} - -status_t flash_impl_erase_resume(FlashAddress sector_addr) { - qspi_flash_erase_resume(QSPI_FLASH, sector_addr); - return S_SUCCESS; -} - -status_t flash_impl_read_sync(void *buffer_ptr, FlashAddress start_addr, size_t buffer_size) { - PBL_ASSERT(buffer_size > 0, "flash_impl_read_sync() called with 0 bytes to read"); - qspi_flash_read_blocking(QSPI_FLASH, start_addr, buffer_ptr, buffer_size); - return S_SUCCESS; -} - -int flash_impl_write_page_begin(const void *buffer, const FlashAddress start_addr, size_t len) { - return qspi_flash_write_page_begin(QSPI_FLASH, buffer, start_addr, len); -} - -status_t flash_impl_get_write_status(void) { return qspi_flash_get_write_status(QSPI_FLASH); } - -status_t flash_impl_enter_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, true); - return S_SUCCESS; -} -status_t flash_impl_exit_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, false); - return S_SUCCESS; -} - -status_t flash_impl_set_burst_mode(bool burst_mode) { - // NYI - return S_SUCCESS; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, false /* !is_subsector */); -} -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, true /* is_subsector */); -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { return 150; } - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { return 50; } - -status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { - return qspi_flash_read_security_register(QSPI_FLASH, addr, val); -} - -status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { - return qspi_flash_security_register_is_locked(QSPI_FLASH, address, locked); -} - -status_t flash_impl_erase_security_register(uint32_t addr) { - return qspi_flash_erase_security_register(QSPI_FLASH, addr); -} - -status_t flash_impl_write_security_register(uint32_t addr, uint8_t val) { - return qspi_flash_write_security_register(QSPI_FLASH, addr, val); -} - -const FlashSecurityRegisters *flash_impl_security_registers_info(void) { - return qspi_flash_security_registers_info(QSPI_FLASH); -} - -#ifdef CONFIG_RECOVERY_FW -status_t flash_impl_lock_security_register(uint32_t address) { - return qspi_flash_lock_security_register(QSPI_FLASH, address); -} -#endif \ No newline at end of file diff --git a/src/fw/flash_region/filesystem_regions_n25q.h b/src/fw/flash_region/filesystem_regions_n25q.h deleted file mode 100644 index 90f8afd9c2..0000000000 --- a/src/fw/flash_region/filesystem_regions_n25q.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -//! @file filesystem_regions_n25q.h -//! -//! Flash regions used for the filesystem used by the serial flash chip that we're using in -//! Tintin/Bianca. Only included by core/flash_region/filesystem_regions.c - -#if !defined(CONFIG_RECOVERY_FW) -#define FILE_SYSTEM_REGIONS(MACRO_OPERATOR) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_BEGIN, FLASH_REGION_FILESYSTEM_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_2_BEGIN, FLASH_REGION_FILESYSTEM_2_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_3_BEGIN, FLASH_REGION_FILESYSTEM_3_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_4_BEGIN, FLASH_REGION_FILESYSTEM_4_END) \ - MACRO_OPERATOR(FLASH_REGION_EXTRA_FILESYSTEM_BEGIN, FLASH_REGION_EXTRA_FILESYSTEM_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_5_BEGIN, FLASH_REGION_FILESYSTEM_5_END) -#else // Same as normal fw except there is one extra region to erase -#define FILE_SYSTEM_REGIONS(MACRO_OPERATOR) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_BEGIN, FLASH_REGION_FILESYSTEM_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_2_BEGIN, FLASH_REGION_FILESYSTEM_2_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_3_BEGIN, FLASH_REGION_FILESYSTEM_3_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_4_BEGIN, FLASH_REGION_FILESYSTEM_4_END) \ - MACRO_OPERATOR(FLASH_REGION_EXTRA_FILESYSTEM_BEGIN, FLASH_REGION_EXTRA_FILESYSTEM_END) \ - MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_5_BEGIN, FLASH_REGION_FILESYSTEM_5_END) \ - MACRO_OPERATOR(FLASH_REGION_UNUSED0_BEGIN, FLASH_REGION_UNUSED0_END) -#endif diff --git a/src/fw/flash_region/flash_region_n25q.h b/src/fw/flash_region/flash_region_n25q.h deleted file mode 100644 index 980689fb6b..0000000000 --- a/src/fw/flash_region/flash_region_n25q.h +++ /dev/null @@ -1,70 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#define SECTOR_SIZE_BYTES 0x10000 -#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) - -#define SUBSECTOR_SIZE_BYTES 0x1000 -#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) -#define FLASH_FILESYSTEM_BLOCK_SIZE SUBSECTOR_SIZE_BYTES - -// Filesystem layout -/////////////////////////////////////// - -// Scratch space for firmware images (normal and recovery). -// We assume this is 64k aligned... -#define FLASH_REGION_FIRMWARE_SLOT_1_BEGIN 0x0 -#define FLASH_REGION_FIRMWARE_SLOT_1_END 0x80000 // 512k - -// Formerly FLASH_REGION_APP_BEGIN -#define FLASH_REGION_FILESYSTEM_3_BEGIN 0x80000 -#define FLASH_REGION_FILESYSTEM_3_END 0x100000 // 512k - -// Formerly REGISTRY_FLASH -// Use one sector for the shared prf storage -#define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN 0x100000 -#define FLASH_REGION_SHARED_PRF_STORAGE_END 0x110000 // 64k - -// Use one sector to store the factory settings registry -#define FACTORY_REGISTRY_FLASH_BEGIN 0x110000 // -#define FACTORY_REGISTRY_FLASH_END 0x120000 // 64k - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN 0x120000 -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END 0x160000 // 256k - -// Formerly part of the 640k of data-logging, which was added to the filesystem as FILESYSTEM_5 -// Reserved a couple sectors because it's always nice to have a couple free. -#define FLASH_REGION_UNUSED0_BEGIN 0x160000 -#define FLASH_REGION_UNUSED0_END 0x180000 // 128k - -#define FLASH_REGION_FILESYSTEM_5_BEGIN 0x180000 -#define FLASH_REGION_FILESYSTEM_5_END 0x200000 // 512k - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN 0x200000 -#define FLASH_REGION_SAFE_FIRMWARE_END 0x280000 // 512k - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN 0x280000 -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END 0x2c0000 // 256k - -// Formerly FLASH_REGION_RESERVED_BEGIN -#define FLASH_REGION_FILESYSTEM_2_BEGIN 0x2c0000 //64k -#define FLASH_REGION_FILESYSTEM_2_END 0x2d0000 - -#define FLASH_REGION_FILESYSTEM_BEGIN 0x2d0000 -#define FLASH_REGION_FILESYSTEM_END 0x320000 // 320k - -// Formerly FLASH_REGION_APP_RESOURCES_BEGIN -#define FLASH_REGION_FILESYSTEM_4_BEGIN 0x320000 -#define FLASH_REGION_FILESYSTEM_4_END 0x3e0000 // 768k - -#define FLASH_REGION_DEBUG_DB_BEGIN 0x3e0000 -#define FLASH_REGION_DEBUG_DB_END 0x400000 // 128k -#define FLASH_DEBUG_DB_BLOCK_SIZE SECTOR_SIZE_BYTES - -#define FLASH_REGION_EXTRA_FILESYSTEM_BEGIN 0x400000 -#define FLASH_REGION_EXTRA_FILESYSTEM_END BOARD_NOR_FLASH_SIZE - -// 0x400000 is the end of the SPI flash address space. - diff --git a/src/fw/flash_region/flash_region_py25q128ha.h b/src/fw/flash_region/flash_region_py25q128ha.h deleted file mode 100644 index 8dc388bcff..0000000000 --- a/src/fw/flash_region/flash_region_py25q128ha.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#define PAGE_SIZE_BYTES (0x100) - -#define SECTOR_SIZE_BYTES (0x10000) -#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) - -#define SUBSECTOR_SIZE_BYTES (0x1000) -#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) - -#define FLASH_REGION_BASE_ADDRESS 0x12000000 - -// A bit of preprocessor magic to help with automatically calculating flash region addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_DEF(MACRO, arg) \ - MACRO(PTABLE, 0x0010000 /* 64K */, arg) /* 0x12000000 - 0x1200FFFF */ \ - MACRO(BOOTLOADER, 0x0010000 /* 64K */, arg) /* 0x12010000 - 0x1201FFFF */ \ - MACRO(FIRMWARE_SLOT_0, 0x0300000 /* 3072K */, arg) /* 0x12020000 - 0x1231FFFF */ \ - MACRO(FIRMWARE_SLOT_1, 0x0300000 /* 3072K */, arg) /* 0x12320000 - 0x1261FFFF */ \ - MACRO(SYSTEM_RESOURCES_BANK_0, 0x0200000 /* 2048K */, arg) /* 0x12620000 - 0x1281FFFF */ \ - MACRO(SYSTEM_RESOURCES_BANK_1, 0x0200000 /* 2048K */, arg) /* 0x12820000 - 0x12A1FFFF */ \ - MACRO(SAFE_FIRMWARE, 0x0080000 /* 512K */, arg) /* 0x12A20000 - 0x12A9FFFF */ \ - MACRO(FILESYSTEM, 0x0520000 /* 5248K */, arg) /* 0x12AA0000 - 0x12FBFFFF */ \ - MACRO(RSVD1, 0x000F000 /* 60K */, arg) /* 0x12FC0000 - 0x12FCEFFF */ \ - MACRO(DEBUG_DB, 0x0020000 /* 128K */, arg) /* 0x12FCF000 - 0x12FEEFFF */ \ - MACRO(RSVD2, 0x000F000 /* 58K */, arg) /* 0x12FEF000 - 0x12FFDFFF */ \ - MACRO(MFG_INFO, 0x0001000 /* 4K */, arg) /* 0x12FFE000 - 0x12FFEFFF */ \ - MACRO(SHARED_PRF_STORAGE, 0x0001000 /* 4K */, arg) /* 0x12FFF000 - 0x12FFFFFF */ - -#include "flash_region_def_helper.h" - -// Flash region _BEGIN and _END addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_PTABLE_BEGIN FLASH_REGION_START_ADDR(PTABLE) -#define FLASH_REGION_PTABLE_END FLASH_REGION_END_ADDR(PTABLE) - -#define FLASH_REGION_BOOTLOADER_BEGIN FLASH_REGION_START_ADDR(BOOTLOADER) -#define FLASH_REGION_BOOTLOADER_END FLASH_REGION_END_ADDR(BOOTLOADER) - -#define FLASH_REGION_FIRMWARE_SLOT_0_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SLOT_0) -#define FLASH_REGION_FIRMWARE_SLOT_0_END FLASH_REGION_END_ADDR(FIRMWARE_SLOT_0) - -#define FLASH_REGION_FIRMWARE_SLOT_1_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SLOT_1) -#define FLASH_REGION_FIRMWARE_SLOT_1_END FLASH_REGION_END_ADDR(FIRMWARE_SLOT_1) - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_0) -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_0) - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_1) -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_1) - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN FLASH_REGION_START_ADDR(SAFE_FIRMWARE) -#define FLASH_REGION_SAFE_FIRMWARE_END FLASH_REGION_END_ADDR(SAFE_FIRMWARE) - -#define FLASH_REGION_FILESYSTEM_BEGIN FLASH_REGION_START_ADDR(FILESYSTEM) -#define FLASH_REGION_FILESYSTEM_END FLASH_REGION_END_ADDR(FILESYSTEM) -#define FLASH_FILESYSTEM_BLOCK_SIZE SUBSECTOR_SIZE_BYTES - -#define FLASH_REGION_DEBUG_DB_BEGIN FLASH_REGION_START_ADDR(DEBUG_DB) -#define FLASH_REGION_DEBUG_DB_END FLASH_REGION_END_ADDR(DEBUG_DB) -#define FLASH_DEBUG_DB_BLOCK_SIZE SUBSECTOR_SIZE_BYTES - -#define FLASH_REGION_MFG_INFO_BEGIN FLASH_REGION_START_ADDR(MFG_INFO) -#define FLASH_REGION_MFG_INFO_END FLASH_REGION_END_ADDR(MFG_INFO) - -#define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN FLASH_REGION_START_ADDR(SHARED_PRF_STORAGE) -#define FLASH_REGION_SHARED_PRF_STORAGE_END FLASH_REGION_END_ADDR(SHARED_PRF_STORAGE) - -#define BOARD_NOR_FLASH_SIZE FLASH_REGION_START_ADDR(_COUNT) - FLASH_REGION_BASE_ADDRESS - -// Static asserts to make sure everything worked out -////////////////////////////////////////////////////////////////////////////// - -// make sure all the sizes are multiples of the subsector size (4k) -FLASH_REGION_SIZE_CHECK(SUBSECTOR_SIZE_BYTES) - -// make sure the total size is what we expect (16mb) -_Static_assert(BOARD_NOR_FLASH_SIZE == 0x1000000, "Flash size should be 16mb"); diff --git a/src/fw/main.c b/src/fw/main.c index cbb092c84f..d2e65b1de8 100644 --- a/src/fw/main.c +++ b/src/fw/main.c @@ -174,8 +174,6 @@ static void init_drivers(void) { #endif flash_init(); - flash_sleep_when_idle(true); - flash_enable_write_protection(); flash_prf_set_protection(true); uint8_t vibe_cali = mfg_info_get_vibe_cali(); diff --git a/src/fw/services/prf_update/service.c b/src/fw/services/prf_update/service.c index e0fc76392b..486a8f6455 100644 --- a/src/fw/services/prf_update/service.c +++ b/src/fw/services/prf_update/service.c @@ -16,9 +16,6 @@ static void prv_do_update(void) { PBL_LOG_INFO("Updating PRF!"); flash_prf_set_protection(false); - bool saved_sleep_when_idle = flash_get_sleep_when_idle(); - flash_sleep_when_idle(false); - #ifndef CONFIG_PBLBOOT FirmwareDescription description = firmware_storage_read_firmware_description(FLASH_REGION_FIRMWARE_DEST_BEGIN); @@ -62,7 +59,6 @@ static void prv_do_update(void) { done: flash_prf_set_protection(true); - flash_sleep_when_idle(saved_sleep_when_idle); PBL_LOG_DBG("Done!"); } #endif From 2db9d7ea69ac4f21368eede1a81b3da97e319423 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Tue, 8 Sep 2026 15:54:42 +0200 Subject: [PATCH 2/3] drivers/flash: rewrite around pbl_flash devices The flash stack was four layers deep for one part: flash_api.c (global mutex, erase suspend/resume state machine, timers) over flash_impl.h (a global, single-instance driver interface) over qspi_flash.h (an instance API with a per-part command table) over the SoC qspi.c, plus a parallel coredump driver, a second async range-erase engine in flash_erase.c and a third, synchronous one in flash_region.c. Replace it with one device-based API in include/pbl/drivers/flash.h: - struct pbl_flash_device carries geometry, security register info and a pointer to struct pbl_flash_ops; struct pbl_flash_device_state holds the lock and the erase engine state. Drivers embed the device in their own struct and recover it with container_of(). Boards define the device and export it as FLASH. - flash.c is the only generic layer: locking, soft write protection, blank checks, CRC helpers and a single erase engine that serves both pbl_flash_erase() and pbl_flash_erase_async(). Ranges are split into sector and subsector erases, already-blank units are skipped, failed units are retried, and on parts that support it an in-flight erase is suspended for reads and writes and resumed from a timer, as before. - pbl_flash_coredump_init() switches the same API into a no-OS mode (no locks, timers or sleeping) instead of a separate cd_flash driver, so core_dump.c no longer needs the dual-driver switch. - Errors are -errno; write and erase assert on hardware failure as the old API did, since callers never checked. Three drivers implement the ops: nrf5_qspi.c (command tables from nor_part.h, formerly the QSPIFlashPart tables in gd25*.c), sf32lb52_mpi.c (SiFli HAL, XIP) and qemu.c. Drivers whose erase completes synchronously leave erase_status NULL, which also removes a latent slowdown: the old blocking erase slept for 7/8 of the typical erase time before its first poll even when the driver had already finished, so every sector erase on the SF32 boards cost an extra ~130 ms. Consumers now call pbl_flash_*(FLASH, ...). The four-argument "optimal range" erase helpers become pbl_flash_erase(addr, len); callers that relied on the upper bound to round to a sector pass the rounded length themselves. flash_prf_set_protection() becomes pbl_flash_protect()/pbl_flash_unprotect() on the PRF region. The SF32 deep-sleep path uses driver-specific pbl_flash_sf32lb52_mpi_dpd_*() instead of reaching into the QSPI port state. The fake flash and the flash driver tests are ported; the flash_region erase tests are dropped with the code they covered. Verified on asterix, obelix, getafix and the QEMU boards (build), with the full unit test suite, and by booting qemu_flint and exercising the erase and unprotect prompt commands. Co-Authored-By: Claude Fable 5.1 Signed-off-by: Gerard Marull-Paretas --- docs/architecture/index.md | 6 +- include/pbl/drivers/flash.h | 313 +++---- include/pbl/drivers/flash/flash_impl.h | 324 -------- include/pbl/drivers/flash/flash_internal.h | 6 - include/pbl/drivers/flash/nor_part.h | 58 ++ include/pbl/drivers/flash/nrf5_qspi.h | 44 + include/pbl/drivers/flash/qemu.h | 16 + include/pbl/drivers/flash/qspi_flash.h | 94 --- .../drivers/flash/qspi_flash_definitions.h | 38 - .../flash/qspi_flash_part_definitions.h | 90 -- include/pbl/drivers/flash/sf32lb52_mpi.h | 36 + include/pbl/drivers/qspi.h | 97 --- include/pbl/drivers/qspi_definitions.h | 51 -- soc/nrf/nrf52/idle.c | 4 +- soc/sf32lb/sf32lb52x/idle.c | 13 +- src/fw/apps/demo/flash_demo.c | 18 +- .../demo/flash_diagnostic/flash_diagnostic.c | 14 +- src/fw/apps/demo/flash_prof/flash_prof.c | 2 +- src/fw/apps/prf/mfg_mic_asterix.c | 12 +- src/fw/apps/prf/mfg_mic_getafix.c | 6 +- src/fw/apps/prf/mfg_mic_obelix.c | 7 +- src/fw/apps/prf/mfg_test_result.c | 14 +- src/fw/board/board_nrf5.h | 2 - src/fw/board/board_qemu.h | 2 - src/fw/board/board_sf32lb52.h | 4 - src/fw/board/boards/board_asterix.c | 34 +- src/fw/board/boards/board_asterix.h | 2 - src/fw/board/boards/board_getafix.c | 34 +- src/fw/board/boards/board_getafix.h | 2 - src/fw/board/boards/board_obelix.c | 34 +- src/fw/board/boards/board_obelix.h | 2 - src/fw/board/boards/board_qemu_emery.c | 17 + src/fw/board/boards/board_qemu_flint.c | 17 + src/fw/board/boards/board_qemu_gabbro.c | 17 + src/fw/console/prompt_commands.c | 97 ++- src/fw/console/pulse_bulkio_coredump.c | 2 +- src/fw/console/pulse_bulkio_external_flash.c | 10 +- src/fw/console/pulse_flash_imaging.c | 21 +- src/fw/debug/flash_logging.c | 27 +- src/fw/drivers/CMakeLists.txt | 4 - src/fw/drivers/flash/CMakeLists.txt | 18 +- src/fw/drivers/flash/Kconfig | 27 +- src/fw/drivers/flash/README.md | 19 +- src/fw/drivers/flash/cd_flash_driver.c | 101 --- src/fw/drivers/flash/flash.c | 524 ++++++++++++ src/fw/drivers/flash/flash_api.c | 491 ----------- src/fw/drivers/flash/flash_crc.c | 61 +- src/fw/drivers/flash/flash_erase.c | 128 --- src/fw/drivers/flash/gd25lq255e.c | 225 +---- src/fw/drivers/flash/gd25q256e.c | 226 +---- src/fw/drivers/flash/nrf5_qspi.c | 652 +++++++++++++++ src/fw/drivers/flash/qemu.c | 96 +++ src/fw/drivers/flash/sf32lb52_mpi.c | 284 +++++++ src/fw/drivers/nrf5/qspi.c | 786 ------------------ src/fw/drivers/otp/otp_flash.c | 64 +- src/fw/drivers/qemu/qemu_flash_hal.c | 265 ------ src/fw/drivers/rtc/sf32lb.c | 12 +- src/fw/drivers/sf32lb52/qspi.c | 401 --------- src/fw/flash_region/filesystem_regions.c | 5 +- src/fw/flash_region/flash_region.c | 104 --- src/fw/flash_region/flash_region.h | 14 - src/fw/kernel/core_dump.c | 55 +- src/fw/kernel/reset.c | 2 +- src/fw/main.c | 6 +- src/fw/mfg/asterix/mfg_info.c | 7 +- src/fw/mfg/getafix/mfg_info.c | 7 +- src/fw/mfg/obelix/mfg_info.c | 7 +- src/fw/mfg/qemu/mfg_info.c | 7 +- src/fw/resource/resource_storage_flash.c | 7 +- .../battery/nrf_fuel_gauge/battery_state.c | 10 +- src/fw/services/data_logging/dls_storage.c | 2 + .../services/filesystem/flash_translation.c | 19 +- src/fw/services/filesystem/pfs.c | 1 + .../get_bytes/get_bytes_storage_coredump.c | 10 +- .../get_bytes/get_bytes_storage_flash.c | 3 +- src/fw/services/prf_update/service.c | 14 +- .../services/process_management/app_storage.c | 1 + .../process_loader_storage.c | 1 + .../put_bytes/put_bytes_storage_raw.c | 13 +- .../shared_prf_storage/shared_prf_storage.c | 21 +- src/fw/system/bootbits.c | 6 +- src/fw/system/firmware_storage.c | 18 +- src/fw/system/version.c | 2 +- tests/fakes/fake_flash_region.c | 64 -- tests/fakes/fake_spi_flash.c | 95 ++- tests/fw/CMakeLists.txt | 7 - tests/fw/applib/CMakeLists.txt | 4 - .../fw/apps/system_apps/health/CMakeLists.txt | 8 - .../apps/system_apps/launcher/CMakeLists.txt | 1 - .../fw/apps/system_apps/music/CMakeLists.txt | 1 - .../apps/system_apps/timeline/CMakeLists.txt | 2 - .../apps/system_apps/weather/CMakeLists.txt | 1 - .../apps/system_apps/workout/CMakeLists.txt | 4 - tests/fw/apps/watch/kickstart/CMakeLists.txt | 1 - tests/fw/drivers/CMakeLists.txt | 9 +- tests/fw/drivers/test_flash_api.c | 433 ++++++---- tests/fw/drivers/test_flash_erase.c | 308 ------- tests/fw/graphics/CMakeLists.txt | 2 - tests/fw/pebble_actions/CMakeLists.txt | 1 - tests/fw/services/CMakeLists.txt | 11 - tests/fw/services/activity/CMakeLists.txt | 2 - tests/fw/services/blob_db/CMakeLists.txt | 6 - tests/fw/services/bluetooth/CMakeLists.txt | 2 - .../fw/services/notifications/CMakeLists.txt | 1 - tests/fw/services/settings/CMakeLists.txt | 1 - tests/fw/services/test_pfs.c | 1 + .../fw/services/test_shared_prf_storage_v3.c | 54 +- tests/fw/services/timeline/CMakeLists.txt | 3 - tests/fw/system/CMakeLists.txt | 9 - tests/fw/system/test_flash_region.c | 227 ----- tests/fw/ui/CMakeLists.txt | 9 - .../flash_region/flash_region.h | 6 - 112 files changed, 2663 insertions(+), 4923 deletions(-) delete mode 100644 include/pbl/drivers/flash/flash_impl.h delete mode 100644 include/pbl/drivers/flash/flash_internal.h create mode 100644 include/pbl/drivers/flash/nor_part.h create mode 100644 include/pbl/drivers/flash/nrf5_qspi.h create mode 100644 include/pbl/drivers/flash/qemu.h delete mode 100644 include/pbl/drivers/flash/qspi_flash.h delete mode 100644 include/pbl/drivers/flash/qspi_flash_definitions.h delete mode 100644 include/pbl/drivers/flash/qspi_flash_part_definitions.h create mode 100644 include/pbl/drivers/flash/sf32lb52_mpi.h delete mode 100644 include/pbl/drivers/qspi.h delete mode 100644 include/pbl/drivers/qspi_definitions.h delete mode 100644 src/fw/drivers/flash/cd_flash_driver.c create mode 100644 src/fw/drivers/flash/flash.c delete mode 100644 src/fw/drivers/flash/flash_api.c delete mode 100644 src/fw/drivers/flash/flash_erase.c create mode 100644 src/fw/drivers/flash/nrf5_qspi.c create mode 100644 src/fw/drivers/flash/qemu.c create mode 100644 src/fw/drivers/flash/sf32lb52_mpi.c delete mode 100644 src/fw/drivers/nrf5/qspi.c delete mode 100644 src/fw/drivers/qemu/qemu_flash_hal.c delete mode 100644 src/fw/drivers/sf32lb52/qspi.c delete mode 100644 src/fw/flash_region/flash_region.c delete mode 100644 tests/fakes/fake_flash_region.c delete mode 100644 tests/fw/drivers/test_flash_erase.c delete mode 100644 tests/fw/system/CMakeLists.txt delete mode 100644 tests/fw/system/test_flash_region.c diff --git a/docs/architecture/index.md b/docs/architecture/index.md index 72812bd558..308593ad52 100644 --- a/docs/architecture/index.md +++ b/docs/architecture/index.md @@ -104,8 +104,10 @@ alongside the allocator in `pfs.c`. between the dumb low-level driver and the accel service that owns buffering, clients and subsampling, so the same service code runs on any accel part. -- **Flash** — `src/fw/drivers/flash/README.md` documents the two flash APIs: - the main one, and a coredump-only path that must work without OS services. +- **Flash** — `include/pbl/drivers/flash.h` is the single flash API: a + `struct pbl_flash_device` with driver `ops`, a generic layer that owns + locking and the erase engine, and a coredump mode that works without OS + services. See `src/fw/drivers/flash/README.md`. ## Coredumps diff --git a/include/pbl/drivers/flash.h b/include/pbl/drivers/flash.h index 1745ae6c92..a3314d7143 100644 --- a/include/pbl/drivers/flash.h +++ b/include/pbl/drivers/flash.h @@ -1,188 +1,141 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ /* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include +#include #include -#include - -#include "system/status_codes.h" - -typedef struct FlashSecurityRegisters { - const uint32_t *sec_regs; - uint8_t num_sec_regs; - uint16_t sec_reg_size; -} FlashSecurityRegisters; - -/** - * Configure the micro's peripherals to communicate with the flash - * chip. - */ -void flash_init(void); - -//! Stop all flash transactions. -void flash_stop(void); - -/** - * Read 1 or more bytes starting at the specified 24bit address into - * the provided buffer. This function does no range checking, so it is - * currently possible to run off the end of the flash. - * - * @param buffer A byte-buffer that will be used to store the data - * read from flash. - * @param start_addr The address of the first byte to be read from flash. - * @param buffer_size The total number of bytes to be read from flash. - */ -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size); - -/** - * Write 1 or more bytes from the buffer to flash starting at the - * specified 24bit address. This function will handle both writing a - * buffer that is larger than the flash's page size and writing to a - * non-page aligned address. - * - * @param buffer A byte-buffer containing the data to be written to flash. - * @param start_addr The address of the first byte to be written to flash. - * @param buffer_size The total number of bytes to be written. - */ -void flash_write_bytes(const uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size); - -typedef void (*FlashOperationCompleteCb)(void *context, status_t result); - -/** - * Erase a subsector asynchronously. - * - * The callback function will be called when the erase completes, whether the - * erase succeeded or failed. The callback will be executed on an arbitrary - * (possibly high-priority) task, so the callback function must return quickly. - * The callback may also be called directly from within flash_erase_subsector. - */ -void flash_erase_subsector(uint32_t subsector_addr, - FlashOperationCompleteCb on_complete, - void *context); - -/** - * Erase a sector asynchronously. - * - * The callback function will be called when the erase completes, whether the - * erase succeeded or failed. The callback will be executed on an arbitrary - * (possibly high-priority) task, so the callback function must return quickly. - * The callback may also be called directly from within flash_erase_sector. - */ -void flash_erase_sector(uint32_t sector_addr, - FlashOperationCompleteCb on_complete, - void *context); - -/** - * Erase the subsector containing the specified address. - */ -void flash_erase_subsector_blocking(uint32_t subsector_addr); - -/** - * Erase the sector containing the specified address. - * - * Beware: this function takes 100ms+ to execute, so be careful when you call it. - */ -void flash_erase_sector_blocking(uint32_t sector_addr); - -/** - * Check whether the sector containing the specified address is already erased. - */ -bool flash_sector_is_erased(uint32_t sector_addr); - -/** - * Check whether the subsector containing the specified address is already erased. - */ -bool flash_subsector_is_erased(uint32_t sector_addr); - -/** - * Erase a region of flash asynchronously using as few erase operations as - * possible. - * - * At least (max_start, min_end) but no more than (min_start, max_end) will be - * erased. Both min_start and max_end must be aligned to a subsector address as - * that is the smallest unit that can be erased. - */ -void flash_erase_optimal_range( - uint32_t min_start, uint32_t max_start, uint32_t min_end, uint32_t max_end, - FlashOperationCompleteCb on_complete, void *context); - -// This is only intended to be called when entering stop mode. It does not use -// any locks because IRQs have already been disabled. The idea is to only incur -// the wait penalty for entering/exiting deep sleep mode for the flash -// before/after stop mode. The flash part consumes ~100uA in standby mode and -// ~10uA when its in deep sleep mode. If the MCU is not in stop mode, this -// difference is negligible -void flash_power_down_for_stop_mode(void); -void flash_power_up_after_stop_mode(void); - -// Returns the sector address that the given flash address lies in -uint32_t flash_get_sector_base_address(uint32_t flash_addr); - -// Returns the subsector address that the given flash address lies in -uint32_t flash_get_subsector_base_address(uint32_t flash_addr); - -// Write-protects the prf region of flash -void flash_prf_set_protection(bool do_protect); - -//! Compute a CRC32 checksum of a region of flash. -uint32_t flash_crc32(uint32_t flash_addr, uint32_t length); - -//! Apply the legacy defective checksum to a region of flash. -uint32_t flash_calculate_legacy_defective_checksum(uint32_t flash_addr, - uint32_t length); - -//! Read security register -//! -//! @param addr The address of the security register to read. -//! -//! @param [out] val The value of the security register read. -//! -//! @retval S_SUCCESS if the read was successful -//! @retval StatusCode if the read failed -status_t flash_read_security_register(uint32_t addr, uint8_t *val); - -//! Check if a security register is locked -//! -//! @param addr The address of the security register to check. -//! @param [out] locked True if the security registers are locked -//! -//! @retval S_SUCCESS if the check was successful -//! @retval StatusCode if the check failed -status_t flash_security_register_is_locked(uint32_t addr, bool *locked); - -//! Erase security register -//! -//! @param addr The address of the security register to erase. -//! -//! @retval S_SUCCESS if the erase was successful -//! @retval StatusCode if the erase failed -status_t flash_erase_security_register(uint32_t addr); - -//! Write security register -//! -//! @param addr The address of the security register to write. -//! -//! @param val The value to write to the security register. -//! -//! @retval S_SUCCESS if the write was successful -//! @retval StatusCode if the write failed -status_t flash_write_security_register(uint32_t addr, uint8_t val); - -//! Obtain security registers information -//! -//! @returns The information about the security registers. -const FlashSecurityRegisters *flash_security_registers_info(void); +#include "pbl/kernel/mutex.h" +#include "pbl/kernel/sem.h" +#include "pbl/services/new_timer/new_timer.h" + +struct pbl_flash_device; + +typedef void (*pbl_flash_erase_cb_t)(void *ctx, int status); + +//! One-time-programmable "security registers" of NOR parts that have them. +struct pbl_flash_sec_regs { + const uint32_t *addrs; + uint8_t count; + uint16_t size; +}; + +//! Driver interface. Called with the device lock held (or from coredump/idle +//! context without any OS services); drivers must not block on OS primitives +//! when @ref pbl_flash_device_state::coredump is set. +struct pbl_flash_ops { + int (*init)(const struct pbl_flash_device *dev); + int (*read)(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len); + int (*write)(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, size_t len); + //! Erase @p size bytes (sector_size or subsector_size) at @p addr. When + //! erase_status is NULL the erase completes before returning. + int (*erase_begin)(const struct pbl_flash_device *dev, uint32_t addr, size_t size); + //! @return 0 done, -EBUSY in progress, -EAGAIN suspended, other errors failed. + int (*erase_status)(const struct pbl_flash_device *dev); + //! @return 0 suspended, 1 if the erase had already completed. Optional. + int (*erase_suspend)(const struct pbl_flash_device *dev); + int (*erase_resume)(const struct pbl_flash_device *dev); + //! Called with interrupts disabled around MCU stop mode. Optional. + void (*power_down)(const struct pbl_flash_device *dev); + void (*power_up)(const struct pbl_flash_device *dev); + int (*sec_reg_read)(const struct pbl_flash_device *dev, uint32_t addr, uint8_t *val); + int (*sec_reg_write)(const struct pbl_flash_device *dev, uint32_t addr, uint8_t val); + int (*sec_reg_erase)(const struct pbl_flash_device *dev, uint32_t addr); + int (*sec_reg_is_locked)(const struct pbl_flash_device *dev, uint32_t addr, bool *locked); + int (*sec_reg_lock)(const struct pbl_flash_device *dev, uint32_t addr); +}; + +struct pbl_flash_device_state { + struct pbl_mutex lock; + struct pbl_sem erase_sem; + bool initialized; + bool coredump; + struct { + bool enabled; + uint32_t start; + uint32_t end; + } protect; + struct { + bool in_progress; + bool suspended; + //! The hardware finished before a suspend was attempted. + bool done; + uint32_t next; + uint32_t end; + uint32_t unit; + uint32_t expected_ms; + uint8_t retries; + pbl_flash_erase_cb_t cb; + void *ctx; + } erase; + TimerID poll_timer; + TimerID resume_timer; +}; + +struct pbl_flash_device { + struct pbl_flash_device_state *state; + const struct pbl_flash_ops *ops; + //! Address of the first byte, as seen by the flash API. + uint32_t base; + uint32_t size; + //! Large and small erase units. + uint32_t sector_size; + uint32_t subsector_size; + //! Typical erase durations, used to pace polling of asynchronous erases. + uint16_t sector_erase_ms; + uint16_t subsector_erase_ms; + //! NULL when the part has no security registers. + const struct pbl_flash_sec_regs *sec_regs; +}; + +//! The board's storage flash. +extern const struct pbl_flash_device *const FLASH; + +int pbl_flash_init(const struct pbl_flash_device *dev); + +//! Re-initialise the device for use from a fault handler: no locking, no +//! sleeping, no timers. Not reversible. +int pbl_flash_coredump_init(const struct pbl_flash_device *dev); + +//! Wait for an in-progress erase to finish. Called before reset. +void pbl_flash_stop(const struct pbl_flash_device *dev); + +//! Reads and writes are thread safe and may be issued while an erase is in +//! progress. Write and erase assert on hardware failure. +int pbl_flash_read(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len); +int pbl_flash_write(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, size_t len); + +//! Erase [addr, addr + len). @p addr must be subsector aligned; @p len is +//! rounded up to a subsector. Sector erases are used wherever the range allows. +int pbl_flash_erase(const struct pbl_flash_device *dev, uint32_t addr, size_t len); + +//! Like @ref pbl_flash_erase, completing through @p cb from an arbitrary task. +//! The callback may also run before this function returns. Blocks while +//! another erase is ongoing. +int pbl_flash_erase_async(const struct pbl_flash_device *dev, uint32_t addr, size_t len, + pbl_flash_erase_cb_t cb, void *ctx); + +bool pbl_flash_is_erased(const struct pbl_flash_device *dev, uint32_t addr, size_t len); + +//! Refuse writes and erases in [addr, addr + len). Only one range at a time. +int pbl_flash_protect(const struct pbl_flash_device *dev, uint32_t addr, size_t len); +int pbl_flash_unprotect(const struct pbl_flash_device *dev); + +//! Around MCU stop mode, with interrupts disabled. +void pbl_flash_power_down(const struct pbl_flash_device *dev); +void pbl_flash_power_up(const struct pbl_flash_device *dev); + +uint32_t pbl_flash_crc32(const struct pbl_flash_device *dev, uint32_t addr, size_t len); +uint32_t pbl_flash_legacy_checksum(const struct pbl_flash_device *dev, uint32_t addr, size_t len); + +//! Security registers. -ENOTSUP when the part has none. +int pbl_flash_sec_reg_read(const struct pbl_flash_device *dev, uint32_t addr, uint8_t *val); +int pbl_flash_sec_reg_write(const struct pbl_flash_device *dev, uint32_t addr, uint8_t val); +int pbl_flash_sec_reg_erase(const struct pbl_flash_device *dev, uint32_t addr); +int pbl_flash_sec_reg_is_locked(const struct pbl_flash_device *dev, uint32_t addr, bool *locked); #ifdef CONFIG_RECOVERY_FW -//! Lock security register -//! -//! @warning This is a one time operation and will permanently lock the security registers. -//! -//! @param addr The address of the security register to lock. -//! -//! @retval S_SUCCESS if the lock was successful -//! @retval StatusCode if the lock failed -status_t flash_lock_security_register(uint32_t addr); -#endif // CONFIG_RECOVERY_FW +//! Permanently locks the register. One-time operation. +int pbl_flash_sec_reg_lock(const struct pbl_flash_device *dev, uint32_t addr); +#endif diff --git a/include/pbl/drivers/flash/flash_impl.h b/include/pbl/drivers/flash/flash_impl.h deleted file mode 100644 index ad2d0da7df..0000000000 --- a/include/pbl/drivers/flash/flash_impl.h +++ /dev/null @@ -1,324 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include -#include -#include - -#include -#include "system/status_codes.h" - -//! Flash Low-Level API -//! -//! Unless otherwise specified, this API is non-reentrant. It is unsafe to -//! call a function in one thread while another function is being executed in a -//! second thread, and it is unsafe to call these functions from within a -//! flash_impl callback. - -typedef uint32_t FlashAddress; - -//! Initialize the low-level flash implementation and hardware into a known -//! state where it is ready to accept commands. -//! -//! @param coredump_mode True if we need this flash driver to not rely on any other system -//! services such as FreeRTOS being available because we're in the middle -//! of a core dump. This may result in slower operations. -status_t flash_impl_init(bool coredump_mode); - -//! Enable or disable synchronous burst mode, if supported. -//! -//! Burst mode is disabled whenever \ref flash_impl_init is called. -//! -//! The result is undefined if this function is called while any other flash -//! operation is in progress. -status_t flash_impl_set_burst_mode(bool enable); - -//! Return the base address of the sector overlapping the given address. -//! -//! This function is reentrant. -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr); - -//! Return the base address of the subsector overlapping the given address. -//! -//! This function is reentrant. -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr); - -//! Query the flash hardware for its capacity in bytes. -size_t flash_impl_get_capacity(void); - -//! Enter a low-power state. -//! -//! Once in a low-power mode, all operations may fail until -//! \ref flash_impl_exit_low_power_mode is called. This function is idempotent. -status_t flash_impl_enter_low_power_mode(void); - -//! Exit a low-power state. -//! -//! Return the flash to a fully operational mode. This may be a time-intensive -//! operation. This function is idempotent. -status_t flash_impl_exit_low_power_mode(void); - -//! Read data into a buffer. -//! -//! The result is undefined if this function is called while a write or erase is -//! in progress. -status_t flash_impl_read_sync(void *buffer, FlashAddress addr, size_t len); - -//! Initiate a DMA-accelerated flash read. -//! -//! The caller must ensure that the DMA transfer will not be interfered with -//! by any clock changes or stoppages externally. (read: inhibit stop mode) -//! -//! This function will return immediately once the transfer has begun. -//! \ref flash_impl_on_read_dma_complete_from_isr will be called from an -//! interrupt context to signal that the transfer has completed. The effect of -//! calling flash_impl_read_dma_begin a second time while another DMA transfer -//! is currently in progress is undefined. -//! -//! The result is undefined if this function is called while a write or erase is -//! in progress. -status_t flash_impl_read_dma_begin(void *buffer, FlashAddress addr, - size_t len); - -//! Called from an interrupt context when the DMA read has completed. It is -//! guaranteed that the call is made from an interrupt of low enough priority -//! that RTOS API calls are safe to use, and that it is a tail-call from the end -//! of the implementation's ISR (read: kernel calls are permissible). -//! -//! @param result S_SUCCESS iff the read completed successfully. -extern void flash_impl_on_read_dma_complete_from_isr(status_t result); - -//! If the flash part requires write protection to be explicitly enabled, enable it. -void flash_impl_enable_write_protection(void); - -//! Write protect a region of flash. Only one region may be protected at any -//! given time. -//! -//! The result is undefined if this function is called while a write or erase is -//! in progress. -status_t flash_impl_write_protect(FlashAddress start_sector, - FlashAddress end_sector); - -//! Remove write protection. -//! -//! The result is undefined if this function is called while a write or erase is -//! in progress. -status_t flash_impl_unprotect(void); - -//! Write a page of bytes to flash. -//! -//! @param buffer The source buffer. -//! @param addr Destination flash address. -//! @param len Length to write. -//! @return The number of bytes that will be written to flash, assuming that the -//! write completes successfully, or a StatusCode error if there was an -//! error starting the write operation. -//! -//! Each call to flash_impl_write_page_begin begins a single flash write -//! operation, writing the maximum amount of data supported by the hardware in -//! a single operation. Multiple page writes may be required to write a complete -//! buffer to flash. -//! -//! Example usage: -//! \code -//! while (len) { -//! int written = flash_impl_write_page_begin(buffer, addr, len)); -//! if (written < 0) { -//! // Handle error -//! } -//! status_t status; -//! while ((status = flash_impl_get_write_status()) == E_AGAIN) { -//! continue; -//! } -//! if (status != S_SUCCESS) { -//! // Handle error -//! } -//! buffer += written; -//! addr += written; -//! len -= written; -//! } -//! \endcode -//! -//! The result is undefined if this function is called while a read or erase is -//! in progress. It is an error to call this function while a write is -//! in progress or suspended. -int flash_impl_write_page_begin(const void *buffer, FlashAddress addr, - size_t len); - -//! Poll the status of a flash page write. -//! -//! @return S_SUCCESS if the write has succeeded, E_ERROR if the write has -//! failed, E_BUSY if the write is still in progress or E_AGAIN if the -//! write is suspended. -status_t flash_impl_get_write_status(void); - -//! Suspend an in-progress write so that reads and erases are permitted. -//! -//! @param addr The address passed to the \ref flash_impl_write_page_begin -//! call which initiated the write being suspended. -status_t flash_impl_write_suspend(FlashAddress addr); - -//! Resume a previously-suspended write. -//! -//! @param addr The address passed to \ref flash_impl_write_suspend. -//! -//! The result is undefined if this function is called while a read or write is -//! in progress. -status_t flash_impl_write_resume(FlashAddress addr); - -//! Erase the subsector which overlaps the given address. -//! -//! The result is undefined if this function is called while a read or write is -//! in progress. It is an error to call this function while an erase is -//! suspended. -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr); - -//! Erase the sector which overlaps the given address. -//! -//! The result is undefined if this function is called while a read or write is -//! in progress. It is an error to call this function while an erase is -//! suspended. -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr); - -//! Erase the entire flash. -//! -//! The result is undefined if this function is called while a read or write is -//! in progress. It is an error to call this function while an erase is -//! suspended. -status_t flash_impl_erase_bulk_begin(void); - -//! Poll the status of a flash erase. -//! -//! @return S_SUCCESS if the erase has succeeded, E_ERROR if the erase has -//! failed, E_BUSY if the erase is still in progress or E_AGAIN if the -//! erase is suspended. -status_t flash_impl_get_erase_status(void); - -//! Returns the typical duration of a subsector erase, in milliseconds. -//! -//! This function is reentrant. -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void); - -//! Returns the typical duration of a sector erase, in milliseconds. -//! -//! This function is reentrant. -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void); - -//! Suspend an in-progress erase so that reads and writes are permitted. -//! -//! @param addr The sector address passed to the -//! \ref flash_impl_erase_subsector_begin or -//! \ref flash_impl_erase_sector_begin call which initiated the erase being -//! suspended. -//! -//! @return S_SUCCESS if the erase has been suspended, S_NO_ACTION_REQUIRED if -//! there was no erase in progress at the time, or an error code. -status_t flash_impl_erase_suspend(FlashAddress addr); - -//! Resume a previously-suspended erase. -//! -//! @param addr The address passed to \ref flash_impl_erase_suspend. -//! -//! The result is undefined if this function is called while a read or write is -//! in progress. -status_t flash_impl_erase_resume(FlashAddress addr); - -//! Check whether the subsector overlapping the specified address is blank -//! (reads as all 1's). -//! -//! @param addr An address within the subsector being checked. -//! -//! @return S_TRUE if blank, S_FALSE if any bit in the sector has been -//! programmed, or E_BUSY if another flash operation is in progress. -//! -//! This operation is hardware-accelerated if possible. This operation may not -//! be performed if any reads, writes, or erases are in progress or suspended, -//! and this operation cannot be suspended once initiated. The result is -//! undefined if any other flash operation is initiated or in progress while a -//! blank check operation is in progress. -//! -//! @warning This function may return S_TRUE on a subsector where an erase -//! operation was terminated prematurely. While such a subsector may -//! read back as blank, data loss may occur and writes may fail if the -//! subsector is not erased fully before it is written to. -status_t flash_impl_blank_check_subsector(FlashAddress addr); - -//! Check whether the sector overlapping the specified address is blank (reads -//! as all 1's). -//! -//! @param addr An address within the sector being checked. -//! -//! @return S_TRUE if blank, S_FALSE if any bit in the sector has been -//! programmed, or E_BUSY if another flash operation is in progress. -//! -//! This operation is hardware-accelerated if possible. This operation may not -//! be performed if any reads, writes, or erases are in progress or suspended, -//! and this operation cannot be suspended once initiated. The result is -//! undefined if any other flash operation is initiated or in progress while a -//! blank check operation is in progress. -//! -//! @warning This function may return S_TRUE on a sector where an erase -//! operation was terminated prematurely. While such a sector may read -//! back as blank, data loss may occur and writes may fail if the -//! sector is not erased fully before it is written to. -status_t flash_impl_blank_check_sector(FlashAddress addr); - -void flash_impl_use(void); -void flash_impl_release(void); -void flash_impl_release_many(uint32_t num_locks); - -//! Read security register -//! -//! @param addr The address of the security register to read. -//! -//! @param [out] val The value of the security register read. -//! -//! @retval S_SUCCESS if the read was successful -//! @retval StatusCode if the read failed -status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val); - -//! Check if a security register is locked -//! -//! @param address The address of the security register to check. -//! @param [out] locked True if the security registers are locked -//! -//! @retval S_SUCCESS if the check was successful -//! @retval StatusCode if the check failed -status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked); - -//! Erase security register -//! -//! @param addr The address of the security register to erase. -//! -//! @retval S_SUCCESS if the erase was successful -//! @retval StatusCode if the erase failed -status_t flash_impl_erase_security_register(uint32_t addr); - -//! Write security register -//! -//! @param addr The address of the security register to write. -//! -//! @param val The value to write to the security register. -//! -//! @retval S_SUCCESS if the write was successful -//! @retval StatusCode if the write failed -status_t flash_impl_write_security_register(uint32_t addr, uint8_t val); - -//! Obtain security registers information -//! -//! @returns The information about the security registers. -const FlashSecurityRegisters *flash_impl_security_registers_info(void); - -#ifdef CONFIG_RECOVERY_FW -//! Lock security register -//! -//! @warning This is a one time operation and will permanently lock the security registers. -//! -//! @param address The address of the security register to lock. -//! -//! @retval S_SUCCESS if the lock was successful -//! @retval StatusCode if the lock failed -status_t flash_impl_lock_security_register(uint32_t address); -#endif // CONFIG_RECOVERY_FW diff --git a/include/pbl/drivers/flash/flash_internal.h b/include/pbl/drivers/flash/flash_internal.h deleted file mode 100644 index dc2b789b4d..0000000000 --- a/include/pbl/drivers/flash/flash_internal.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -void flash_erase_init(void); diff --git a/include/pbl/drivers/flash/nor_part.h b/include/pbl/drivers/flash/nor_part.h new file mode 100644 index 0000000000..d325769513 --- /dev/null +++ b/include/pbl/drivers/flash/nor_part.h @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include + +//! JESD216 BFP DW15 quad-enable requirement. +enum pbl_flash_nor_qer { + PBL_FLASH_NOR_QER_NONE = 0, + PBL_FLASH_NOR_QER_S2B1v1 = 1, + PBL_FLASH_NOR_QER_S1B6 = 2, + PBL_FLASH_NOR_QER_S2B7 = 3, + PBL_FLASH_NOR_QER_S2B1v4 = 4, + PBL_FLASH_NOR_QER_S2B1v5 = 5, + PBL_FLASH_NOR_QER_S2B1v6 = 6, +}; + +//! Command set of a JEDEC SPI NOR part, for controllers that issue the +//! commands themselves. +struct pbl_flash_nor_part { + const char *name; + uint32_t id; + uint32_t size; + struct { + uint8_t write_enable; + uint8_t rdsr1; + uint8_t rdsr2; + uint8_t wrsr; + uint8_t wrsr2; + uint8_t erase_suspend; + uint8_t erase_resume; + uint8_t enter_low_power; + uint8_t exit_low_power; + uint8_t reset_enable; + uint8_t reset; + uint8_t read_id; + uint8_t en4b; + uint8_t erase_sec; + uint8_t program_sec; + uint8_t read_sec; + } cmd; + struct { + uint8_t sr1_busy; + uint8_t sr2_erase_suspend; + } mask; + uint32_t reset_latency_ms; + uint32_t suspend_to_read_latency_us; + uint32_t standby_to_low_power_latency_us; + uint32_t low_power_to_standby_latency_us; + enum pbl_flash_nor_qer qer; + struct pbl_flash_sec_regs sec_regs; +}; + +extern const struct pbl_flash_nor_part pbl_flash_nor_gd25lq255e; +extern const struct pbl_flash_nor_part pbl_flash_nor_gd25q256e; diff --git a/include/pbl/drivers/flash/nrf5_qspi.h b/include/pbl/drivers/flash/nrf5_qspi.h new file mode 100644 index 0000000000..fb4f28f8ac --- /dev/null +++ b/include/pbl/drivers/flash/nrf5_qspi.h @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include +#include +#include "pbl/kernel/sem.h" + +enum pbl_flash_nrf5_qspi_read_mode { + PBL_FLASH_NRF5_QSPI_READ_FASTREAD, + PBL_FLASH_NRF5_QSPI_READ_READ2O, + PBL_FLASH_NRF5_QSPI_READ_READ2IO, + PBL_FLASH_NRF5_QSPI_READ_READ4O, + PBL_FLASH_NRF5_QSPI_READ_READ4IO, +}; + +enum pbl_flash_nrf5_qspi_write_mode { + PBL_FLASH_NRF5_QSPI_WRITE_PP, + PBL_FLASH_NRF5_QSPI_WRITE_PP2O, + PBL_FLASH_NRF5_QSPI_WRITE_PP4O, + PBL_FLASH_NRF5_QSPI_WRITE_PP4IO, +}; + +struct pbl_flash_nrf5_qspi_state { + struct pbl_flash_device_state flash; + struct pbl_sem sem; + bool initialized; +}; + +struct pbl_flash_nrf5_qspi { + struct pbl_flash_device dev; + const struct pbl_flash_nor_part *part; + uint32_t clk_freq_hz; + uint32_t cs_gpio; + uint32_t clk_gpio; + uint32_t data_gpio[4]; + enum pbl_flash_nrf5_qspi_read_mode read_mode; + enum pbl_flash_nrf5_qspi_write_mode write_mode; +}; + +extern const struct pbl_flash_ops pbl_flash_nrf5_qspi_ops; diff --git a/include/pbl/drivers/flash/qemu.h b/include/pbl/drivers/flash/qemu.h new file mode 100644 index 0000000000..8580717dcf --- /dev/null +++ b/include/pbl/drivers/flash/qemu.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include + +struct pbl_flash_qemu { + struct pbl_flash_device dev; + //! Controller registers of the QEMU pebble-extflash device. + uintptr_t regs; +}; + +extern const struct pbl_flash_ops pbl_flash_qemu_ops; diff --git a/include/pbl/drivers/flash/qspi_flash.h b/include/pbl/drivers/flash/qspi_flash.h deleted file mode 100644 index 54d8ba6d77..0000000000 --- a/include/pbl/drivers/flash/qspi_flash.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include -#include - -#include -#include -#include "system/status_codes.h" - -//! Initialize the QSPI flash -//! @param dev The QSPI flash device to initialize -//! @param part The description of the QSPI flash part -//! @param coredump_mode If true, don't use anything that might not be available mid-crash, such -//! as FreeRTOS calls or other system services. -void qspi_flash_init(QSPIFlash *dev, QSPIFlashPart *part, bool coredump_mode); - -bool qspi_flash_is_in_coredump_mode(QSPIFlash *dev); - -//! Check if the WHOAMI matches the expected value -bool qspi_flash_check_whoami(QSPIFlash *dev); - -//! Check if an in-progress erase is complete -status_t qspi_flash_is_erase_complete(QSPIFlash *dev); - -//! Begin an erase -status_t qspi_flash_erase_begin(QSPIFlash *dev, uint32_t addr, bool is_subsector); - -//! Suspend an erase -status_t qspi_flash_erase_suspend(QSPIFlash *dev, uint32_t addr); - -//! Resume a suspended erase -void qspi_flash_erase_resume(QSPIFlash *dev, uint32_t addr); - -//! Performs a blocking read -void qspi_flash_read_blocking(QSPIFlash *dev, uint32_t addr, void *buffer, uint32_t length); - -// Begins a write operation -int qspi_flash_write_page_begin(QSPIFlash *dev, const void *buffer, uint32_t addr, uint32_t length); - -//! Gets the status of an in-progress write operation -status_t qspi_flash_get_write_status(QSPIFlash *dev); - -//! Sets whether or not the QSPI flash is in low-power mode -void qspi_flash_set_lower_power_mode(QSPIFlash *dev, bool active); - -//! Check whether a sector/subsector is blank -status_t qspi_flash_blank_check(QSPIFlash *dev, uint32_t addr, bool is_subsector); - -//! Sets the values of the bits (masked by `mask`) in the register (read by `read_instruction` and -//! written via `write_instruction`) to `value` -void qspi_flash_ll_set_register_bits(QSPIFlash *dev, uint8_t read_instruction, - uint8_t write_instruction, uint8_t value, uint8_t mask); - -//! Enable write/erase protection on the given QSPI flash part. -//! Requires the `write_protection_enable` and `read_protection_status` instructions. -//! Return value of the `read_protection_status` instruction is checked against -//! `block_lock.protection_enabled_mask` to test for success. -status_t qspi_flash_write_protection_enable(QSPIFlash *dev); - -//! Lock the given sector from write/erase operations. -//! Sector locked with the `block_lock` instruction, and confirmed with `block_lock_status` -//! If the `block_lock` instruction requires extra data, `block_lock.has_lock_data` -//! and `block_lock.lock_data` can be used. -//! When checking `block_lock_status`, the returned status value is -//! compared against `block_lock.locked_check` -status_t qspi_flash_lock_sector(QSPIFlash *dev, uint32_t addr); - -//! Unlock all sectors so they can be written/erased. -//! Operation is performed by the `block_unlock_all` instruction. -status_t qspi_flash_unlock_all(QSPIFlash *dev); - -//! Read security register -status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_t *val); - -//! Check if the given security registers is locked -status_t qspi_flash_security_register_is_locked(QSPIFlash *dev, uint32_t address, bool *locked); - -//! Erase security register -status_t qspi_flash_erase_security_register(QSPIFlash *dev, uint32_t addr); - -//! Write security register -status_t qspi_flash_write_security_register(QSPIFlash *dev, uint32_t addr, uint8_t val); - -//! Obtain security registers information -const FlashSecurityRegisters *qspi_flash_security_registers_info(QSPIFlash *dev); - -#ifdef CONFIG_RECOVERY_FW -//! Lock security registers -//! @warning This is a one time operation and will permanently lock the security registers. -status_t qspi_flash_lock_security_register(QSPIFlash *dev, uint32_t address); -#endif // CONFIG_RECOVERY_FW diff --git a/include/pbl/drivers/flash/qspi_flash_definitions.h b/include/pbl/drivers/flash/qspi_flash_definitions.h deleted file mode 100644 index 9819af31c5..0000000000 --- a/include/pbl/drivers/flash/qspi_flash_definitions.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include "board/board.h" -#include -#include "qspi_flash_part_definitions.h" - -typedef struct QSPIFlashState { - QSPIFlashPart *part; - bool coredump_mode; - bool fast_read_ddr_enabled; -} QSPIFlashState; - -typedef enum QSPIFlashReadMode { - QSPI_FLASH_READ_FASTREAD, - QSPI_FLASH_READ_READ2O, - QSPI_FLASH_READ_READ2IO, - QSPI_FLASH_READ_READ4O, - QSPI_FLASH_READ_READ4IO, -} QSPIFlashReadMode; - -typedef enum QSPIFlashWriteMode { - QSPI_FLASH_WRITE_PP, - QSPI_FLASH_WRITE_PP2O, - QSPI_FLASH_WRITE_PP4O, - QSPI_FLASH_WRITE_PP4IO, -} QSPIFlashWriteMode; - -typedef const struct QSPIFlash { - QSPIFlashState *state; - QSPIPort *qspi; - bool default_fast_read_ddr_enabled; - QSPIFlashReadMode read_mode; - QSPIFlashWriteMode write_mode; - OutputConfig reset_gpio; -} QSPIFlash; diff --git a/include/pbl/drivers/flash/qspi_flash_part_definitions.h b/include/pbl/drivers/flash/qspi_flash_part_definitions.h deleted file mode 100644 index e4479160cc..0000000000 --- a/include/pbl/drivers/flash/qspi_flash_part_definitions.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include -#include - -#include - -typedef enum JESD216Dw15QerType { - JESD216_DW15_QER_NONE = 0, - JESD216_DW15_QER_S2B1v1 = 1, - JESD216_DW15_QER_S1B6 = 2, - JESD216_DW15_QER_S2B7 = 3, - JESD216_DW15_QER_S2B1v4 = 4, - JESD216_DW15_QER_S2B1v5 = 5, - JESD216_DW15_QER_S2B1v6 = 6, -} JESD216Dw15QerType; - -typedef const struct QSPIFlashPart { - struct { - uint8_t fast_read; - uint8_t fast_read_ddr; - uint8_t read2o; - uint8_t read2io; - uint8_t read4o; - uint8_t read4io; - uint8_t pp; - uint8_t pp2o; - uint8_t pp4o; - uint8_t pp4io; - uint8_t erase_sector_4k; - uint8_t erase_block_64k; - uint8_t write_enable; - uint8_t write_disable; - uint8_t rdsr1; - uint8_t rdsr2; - uint8_t wrsr; - uint8_t wrsr2; - uint8_t erase_suspend; - uint8_t erase_resume; - uint8_t enter_low_power; - uint8_t exit_low_power; - uint8_t enter_quad_mode; - uint8_t exit_quad_mode; - uint8_t reset_enable; - uint8_t reset; - uint8_t qspi_id; - uint8_t block_lock; - uint8_t block_lock_status; - uint8_t block_unlock_all; - uint8_t write_protection_enable; - uint8_t read_protection_status; - uint8_t en4b; - uint8_t erase_sec; - uint8_t program_sec; - uint8_t read_sec; - } instructions; - struct { - uint8_t busy; - uint8_t write_enable; - } status_bit_masks; - struct { - uint8_t sec_lock; - uint8_t erase_suspend; - } flag_status_bit_masks; - struct { - uint8_t fast_read; - uint8_t fast_read_ddr; - } dummy_cycles; - struct { - bool has_lock_data; // +#include + +#include + +#include + +struct pbl_flash_sf32lb52_mpi_state { + struct pbl_flash_device_state flash; + QSPI_FLASH_CTX_T ctx; + DMA_HandleTypeDef hdma; + qspi_configure_t cfg; + struct dma_config dma; + bool initialized; +}; + +struct pbl_flash_sf32lb52_mpi { + struct pbl_flash_device dev; + const char *name; + uint32_t id; + uint16_t clk_div; + //! Deep power-down entry / exit latencies of the part. + uint32_t dpd_enter_us; + uint32_t dpd_exit_us; +}; + +extern const struct pbl_flash_ops pbl_flash_sf32lb52_mpi_ops; + +//! Deep power-down around SoC deep sleep. Called with interrupts disabled. +void pbl_flash_sf32lb52_mpi_dpd_enter(const struct pbl_flash_device *dev); +void pbl_flash_sf32lb52_mpi_dpd_exit(const struct pbl_flash_device *dev); diff --git a/include/pbl/drivers/qspi.h b/include/pbl/drivers/qspi.h deleted file mode 100644 index 316b35e360..0000000000 --- a/include/pbl/drivers/qspi.h +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include -#include - -#include - -//! Memory mapped region for the QSPI controller -#define QSPI_MMAP_BASE_ADDRESS ((uintptr_t) 0x90000000) - -//! Timouts for qspi_poll_bit -#define QSPI_NO_TIMEOUT (0) - -//! Enable the peripheral clock -void qspi_use(QSPIPort *dev); - -//! Disable the peripheral clock -void qspi_release(QSPIPort *dev); - -//! Perform an indirect read operation -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -//! @param dummy_cycles How many cycles to wait before reading data -//! @param buffer The buffer to read into -//! @param length The number of bytes to read -//! @param is_ddr Whether to use DDR (double data rate) mode -void qspi_indirect_read_no_addr(QSPIPort *dev, uint8_t instruction, uint8_t dummy_cycles, - void *buffer, uint32_t length, bool is_ddr); - -//! Perform an indirect read operation with an address -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -//! @param addr The address to read from -//! @param dummy_cycles How many cycles to wait before reading data -//! @param buffer The buffer to read into -//! @param length The number of bytes to read -//! @param is_ddr Whether to use DDR (double data rate) mode -void qspi_indirect_read(QSPIPort *dev, uint8_t instruction, uint32_t addr, uint8_t dummy_cycles, - void *buffer, uint32_t length, bool is_ddr); - -//! Performs an indirect read operation with DMA -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -//! @param start_addr The address to read from -//! @param dummy_cycles How many cycles to wait before reading data -//! @param buffer The buffer to read into -//! @param length The number of bytes to read -//! @param is_ddr Whether to use DDR (double data rate) mode -void qspi_indirect_read_dma(QSPIPort *dev, uint8_t instruction, uint32_t start_addr, - uint8_t dummy_cycles, void *buffer, uint32_t length, bool is_ddr); - -//! Perform an indirect write operation -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -//! @param buffer The buffer to write from or NULL if no data should be written -//! @param length The number of bytes to write or 0 if no data should be written -void qspi_indirect_write_no_addr(QSPIPort *dev, uint8_t instruction, const void *buffer, - uint32_t length); - -//! Perform an indirect write operation with an address -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -//! @param addr The address to write to -//! @param buffer The buffer to write from or NULL if no data should be written -//! @param length The number of bytes to write or 0 if no data should be written -void qspi_indirect_write(QSPIPort *dev, uint8_t instruction, uint32_t addr, const void *buffer, - uint32_t length); - -//! Perform an indirect write operation in single SPI mode (not quad SPI) -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -void qspi_indirect_write_no_addr_1line(QSPIPort *dev, uint8_t instruction); - -//! Perform an automatic poll operation which will wait for the specified bits to be set/cleared -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -//! @param bit_mask The bit(s) to poll on (wait for) -//! @param should_be_set Whether the bits should be set or cleared (true / false respectively) -//! @param timeout_us The maximum amount of time to wait in us -bool qspi_poll_bit(QSPIPort *dev, uint8_t instruction, uint8_t bit_mask, bool should_be_set, - uint32_t timeout_us); - -//! Puts the QSPI in memory-mapped mode -//! @param dev The QSPI port to use -//! @param instruction The instruction to issue -//! @param addr address of data that will be accessed via memory mapping -//! @param dummy_cycles How many cycles to wait before we can start reading the mapped memory -//! @param length length of data that will be accessed via memory mapping -//! @param is_ddr Whether to use DDR (double data rate) mode -void qspi_mmap_start(QSPIPort *dev, uint8_t instruction, uint32_t addr, uint8_t dummy_cycles, - uint32_t length, bool is_ddr); - -//! Aborts the memory-mapped mode -void qspi_mmap_stop(QSPIPort *dev); diff --git a/include/pbl/drivers/qspi_definitions.h b/include/pbl/drivers/qspi_definitions.h deleted file mode 100644 index a9d7020da9..0000000000 --- a/include/pbl/drivers/qspi_definitions.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include "pbl/kernel/sem.h" -#include "board/board.h" - - -#include -#include - -#ifdef CONFIG_SOC_SF32LB52 -#include "bf0_hal_dma.h" -#include "bf0_hal_mpi.h" -#endif - -#define QSPI_NUM_DATA_PINS (4) - -typedef struct QSPIPortState { -#ifdef CONFIG_SOC_NRF52 - struct pbl_sem sem; - bool initialized; -#elif defined(CONFIG_SOC_SF32LB52) - QSPI_FLASH_CTX_T ctx; - DMA_HandleTypeDef hdma; - qspi_configure_t cfg; - struct dma_config dma; - uint32_t t_enter_deep_us; - uint32_t t_exit_deep_us; - bool initialized; -#else - struct pbl_sem dma_semaphore; - int use_count; -#endif -} QSPIPortState; - -typedef const struct QSPIPort { - QSPIPortState *state; -#ifdef CONFIG_SOC_NRF52 - uint32_t clk_freq_hz; - uint32_t cs_gpio; - uint32_t clk_gpio; - uint32_t data_gpio[QSPI_NUM_DATA_PINS]; -#elif defined(CONFIG_SOC_SF32LB52) - uint16_t clk_div; -#endif -} QSPIPort; - -//! Initialize the QSPI peripheral, the pins, and the DMA -void qspi_init(QSPIPort *dev, uint32_t flash_size); diff --git a/soc/nrf/nrf52/idle.c b/soc/nrf/nrf52/idle.c index 075a57452b..42786f8088 100644 --- a/soc/nrf/nrf52/idle.c +++ b/soc/nrf/nrf52/idle.c @@ -48,7 +48,7 @@ void pbl_soc_idle(pbl_tick_t max_ticks) { const RtcTicks sleep_ticks = max_ticks - EARLY_WAKEUP_TICKS; RtcTicks elapsed_ticks; - flash_power_down_for_stop_mode(); + pbl_flash_power_down(FLASH); rtc_alarm_set(sleep_ticks); rtc_systick_pause(); @@ -65,7 +65,7 @@ void pbl_soc_idle(pbl_tick_t max_ticks) { elapsed_ticks = rtc_alarm_get_elapsed_ticks(); pbl_idle_slept(elapsed_ticks); - flash_power_up_after_stop_mode(); + pbl_flash_power_up(FLASH); task_watchdog_step_elapsed_time_ms((elapsed_ticks * 1000) / RTC_TICKS_HZ); s_analytics_full_sleep_ticks += elapsed_ticks; diff --git a/soc/sf32lb/sf32lb52x/idle.c b/soc/sf32lb/sf32lb52x/idle.c index 262bd5fce4..8869abe91b 100644 --- a/soc/sf32lb/sf32lb52x/idle.c +++ b/soc/sf32lb/sf32lb52x/idle.c @@ -7,6 +7,7 @@ #include "board/board.h" #include "console/prompt.h" #include "drivers/flash.h" +#include "drivers/flash/sf32lb52_mpi.h" #include "drivers/rtc.h" #include "drivers/sf32lb52/rc10k.h" #include "drivers/task_watchdog.h" @@ -89,7 +90,7 @@ static inline void prv_enter_wfi(void) { static void prv_enter_deepwfi(void) { s_last_sleep_type = SleepTypeDeepWfi; - flash_power_down_for_stop_mode(); + pbl_flash_power_down(FLASH); __DSB(); __ISB(); @@ -100,17 +101,12 @@ static void prv_enter_deepwfi(void) { } static void prv_enter_deepslep(void) { - QSPIPortState *flash_state; uint32_t dll1_freq = 0UL; int clk_src; - flash_state = QSPI_FLASH->qspi->state; - prv_save_iser(); - HAL_FLASH_NOP_CMD(&flash_state->ctx.handle); - HAL_FLASH_DEEP_PWRDOWN(&flash_state->ctx.handle); - HAL_Delay_us(flash_state->t_enter_deep_us); + pbl_flash_sf32lb52_mpi_dpd_enter(FLASH); NVIC_EnableIRQ(AON_IRQn); @@ -158,8 +154,7 @@ static void prv_enter_deepslep(void) { HAL_Delay_us(0); } - HAL_FLASH_RELEASE_DPD(&flash_state->ctx.handle); - HAL_Delay_us(flash_state->t_exit_deep_us); + pbl_flash_sf32lb52_mpi_dpd_exit(FLASH); prv_restore_iser(); } diff --git a/src/fw/apps/demo/flash_demo.c b/src/fw/apps/demo/flash_demo.c index b32684527e..e48163421d 100644 --- a/src/fw/apps/demo/flash_demo.c +++ b/src/fw/apps/demo/flash_demo.c @@ -17,32 +17,32 @@ static Window *window; static void test_write_short(void) { uint16_t buffer; - flash_read_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); + pbl_flash_read(FLASH, BASE_ADDRESS, (uint8_t*) &buffer, sizeof(buffer)); PBL_LOG_DBG(">> Addr 0x%x is 0x%"PRIx16, BASE_ADDRESS, buffer); buffer = 0x0505; - flash_write_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); + pbl_flash_write(FLASH, BASE_ADDRESS, (uint8_t*) &buffer, sizeof(buffer)); PBL_LOG_DBG(">> Addr 0x%x Written to 0x%x", BASE_ADDRESS, buffer); uint8_t read_buffer = 0x0; - flash_read_bytes((uint8_t*) &read_buffer, BASE_ADDRESS, sizeof(read_buffer)); + pbl_flash_read(FLASH, BASE_ADDRESS, (uint8_t*) &read_buffer, sizeof(read_buffer)); PBL_LOG_DBG(">> Addr 0x%x is (8) 0x%"PRIx8, BASE_ADDRESS, read_buffer); buffer = 0x0; - flash_read_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); + pbl_flash_read(FLASH, BASE_ADDRESS, (uint8_t*) &buffer, sizeof(buffer)); PBL_LOG_DBG(">> Addr 0x%x is (16) 0x%"PRIx16, BASE_ADDRESS, buffer); } static void test_write_bytes(void) { for (int i = 1; i < 127; ++i) { uint8_t data = i; - flash_write_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); + pbl_flash_write(FLASH, BASE_ADDRESS + i, (uint8_t*) &data, sizeof(data)); PBL_LOG_DBG(">> Wrote Addr 0x%x is 0x%"PRIx8, i, data); } for (int i = 0; i < 128; ++i) { uint8_t data = 0; - flash_read_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); + pbl_flash_read(FLASH, BASE_ADDRESS + i, (uint8_t*) &data, sizeof(data)); PBL_LOG_DBG(">> Read Addr 0x%x is (8) 0x%"PRIx8, i, data); } } @@ -54,11 +54,11 @@ static void test_write_block(void) { data[i] = i; } - flash_write_bytes(data, BASE_ADDRESS + 31, sizeof(data)); + pbl_flash_write(FLASH, BASE_ADDRESS + 31, data, sizeof(data)); for (int i = 0; i < 128; ++i) { uint8_t data = 0; - flash_read_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); + pbl_flash_read(FLASH, BASE_ADDRESS + i, (uint8_t*) &data, sizeof(data)); PBL_LOG_DBG(">> Read Addr 0x%x is (8) 0x%"PRIx8, i, data); } } @@ -72,7 +72,7 @@ static void do_flash_operation(void) { PBL_LOG_DBG(">> Flash operation time!"); PBL_LOG_DBG(">> Erasing 0x%x", BASE_ADDRESS); - flash_erase_sector_blocking(BASE_ADDRESS); + pbl_flash_erase(FLASH, BASE_ADDRESS, SECTOR_SIZE_BYTES); PBL_LOG_DBG(">> Erasing 0x%x Done", BASE_ADDRESS); test_write_short(); diff --git a/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.c b/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.c index b28d19de33..458035547e 100644 --- a/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.c +++ b/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.c @@ -74,7 +74,7 @@ static bool check_region_erased(struct Region region) { bool success = true; for (uint32_t i = region.begin; i < region.end; i += sizeof(uint32_t)) { uint32_t read = 0; - flash_read_bytes((uint8_t *)&read, i, sizeof(read)); + pbl_flash_read(FLASH, i, (uint8_t *)&read, sizeof(read)); if (read != 0xffffffff) { PBL_LOG_SYNC_INFO(">>>> Address 0x%lx failed to erase: 0x%lx", i, read); success = false; @@ -100,9 +100,9 @@ static bool check_region_write(struct Region region, bool use_rand, uint32_t write = write_rand; uint32_t read = 0xffff; if (perform_writes) { - flash_write_bytes((uint8_t *)&write, i, sizeof(write)); + pbl_flash_write(FLASH, i, (uint8_t *)&write, sizeof(write)); } - flash_read_bytes((uint8_t *)&read, i, sizeof(read)); + pbl_flash_read(FLASH, i, (uint8_t *)&read, sizeof(read)); if (read != write) { PBL_LOG_SYNC_INFO(">>>> Address 0x%lx failed to write: 0x%lx 0x%lx", i, read, write); @@ -147,7 +147,7 @@ static bool check_subsector_bitflip(struct Region region) { uint32_t erase = subsec + i; PBL_ASSERTN((erase % (4 * 1024)) == 0); PBL_LOG_SYNC_INFO("Subsector Erase of 0x%lx", erase); - flash_erase_subsector_blocking(erase); + pbl_flash_erase(FLASH, erase, SUBSECTOR_SIZE_BYTES); } if (!check_region_write(write_region, false, false)) { @@ -163,7 +163,7 @@ static bool check_subsector_bitflip(struct Region region) { static void menu_select_callback(int index, void *data) { struct Region region = s_flash_regions[index]; PBL_LOG_INFO(">>>> Erase %s", region.name); - flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); + pbl_flash_erase(FLASH, region.begin, region.end - region.begin); PBL_LOG_INFO(">>>> Checking '%s' is erased", region.name); check_region_erased(region); PBL_LOG_INFO(">>>> Checking '%s' can write", region.name); @@ -187,7 +187,7 @@ static void app_timer_cb(void *data) { PBL_LOG_INFO(">>>> %s %d", "Test Loop", stress_data.stress_iteration); PBL_LOG_INFO("Erasing 0x%lx to 0x%lx", region.begin, region.end); - flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); + pbl_flash_erase(FLASH, region.begin, region.end - region.begin); bool failed = true; if (stress_data.stress_index == FILE_WRITE_STRESS) { @@ -204,7 +204,7 @@ static void app_timer_cb(void *data) { if (!failed && (stress_data.stress_iteration < num_stress_iters)) { app_timer_register(1000, app_timer_cb, NULL); // allow for animation to complete } else { // clean up state - flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); + pbl_flash_erase(FLASH, region.begin, region.end - region.begin); } } } diff --git a/src/fw/apps/demo/flash_prof/flash_prof.c b/src/fw/apps/demo/flash_prof/flash_prof.c index 289234cd91..88db985edd 100644 --- a/src/fw/apps/demo/flash_prof/flash_prof.c +++ b/src/fw/apps/demo/flash_prof/flash_prof.c @@ -20,7 +20,7 @@ static uint32_t timed_read_bytes(uint32_t num_bytes) { time_t start_time_s; uint16_t start_time_ms; rtc_get_time_ms(&start_time_s, &start_time_ms); - flash_read_bytes(buffer, FLASH_REGION_FILESYSTEM_BEGIN, num_bytes); + pbl_flash_read(FLASH, FLASH_REGION_FILESYSTEM_BEGIN, buffer, num_bytes); time_t stop_time_s; uint16_t stop_time_ms; rtc_get_time_ms(&stop_time_s, &stop_time_ms); diff --git a/src/fw/apps/prf/mfg_mic_asterix.c b/src/fw/apps/prf/mfg_mic_asterix.c index 1cd9ada2c4..87180a22d0 100644 --- a/src/fw/apps/prf/mfg_mic_asterix.c +++ b/src/fw/apps/prf/mfg_mic_asterix.c @@ -238,7 +238,7 @@ static void prv_mic_capture(void) { err = nrfx_pdm_init(&s_pdm, &s_pdm_cfg, prv_pdm_evt_handler); PBL_ASSERTN(err == NRFX_SUCCESS); - flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); + pbl_flash_erase(FLASH, FLASH_START, FLASH_END - FLASH_START); err = nrfx_pdm_start(&s_pdm); PBL_ASSERTN(err == NRFX_SUCCESS); @@ -247,7 +247,7 @@ static void prv_mic_capture(void) { for (unsigned int i = 0U; i < RECORDING_MS / CAPTURE_MS; i++) { pbl_sem_take(&s_data_ready, PBL_FOREVER); - flash_write_bytes((uint8_t *)s_buf_rd, flash_addr, BLOCK_SIZE); + pbl_flash_write(FLASH, flash_addr, (uint8_t *)s_buf_rd, BLOCK_SIZE); flash_addr += BLOCK_SIZE; } @@ -262,7 +262,7 @@ static void prv_mic_capture(void) { flash_addr = FLASH_START; dbgserial_putstr("S"); for (unsigned int i = 0U; i < RECORDING_MS / CAPTURE_MS; i++) { - flash_read_bytes((uint8_t *)s_buf_rd, flash_addr, BLOCK_SIZE); + pbl_flash_read(FLASH, flash_addr, (uint8_t *)s_buf_rd, BLOCK_SIZE); flash_addr += BLOCK_SIZE; for (unsigned int j = 0U; j < N_SAMPLES; j++) { @@ -291,7 +291,7 @@ static void prv_playback(void) { PBL_ASSERTN(err == NRFX_SUCCESS); flash_addr = FLASH_START; - flash_read_bytes((uint8_t *)s_buf[0], flash_addr, BLOCK_SIZE); + pbl_flash_read(FLASH, flash_addr, (uint8_t *)s_buf[0], BLOCK_SIZE); flash_addr += BLOCK_SIZE; s_buf_idx = 1U; @@ -305,7 +305,7 @@ static void prv_playback(void) { prv_codec_setup(); for (unsigned int i = 0U; i < (RECORDING_MS / CAPTURE_MS) - 1U; i++) { - flash_read_bytes((uint8_t *)s_buf_wr, flash_addr, BLOCK_SIZE); + pbl_flash_read(FLASH, flash_addr, (uint8_t *)s_buf_wr, BLOCK_SIZE); flash_addr += BLOCK_SIZE; pbl_sem_take(&s_need_data, PBL_FOREVER); @@ -320,7 +320,7 @@ static void prv_playback(void) { pbl_sem_deinit(&s_need_data); - flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); + pbl_flash_erase(FLASH, FLASH_START, FLASH_END - FLASH_START); } #else static void prv_playback(void) { diff --git a/src/fw/apps/prf/mfg_mic_getafix.c b/src/fw/apps/prf/mfg_mic_getafix.c index c89e431126..dda7851080 100644 --- a/src/fw/apps/prf/mfg_mic_getafix.c +++ b/src/fw/apps/prf/mfg_mic_getafix.c @@ -170,7 +170,7 @@ static void prv_analyze_dual_mic(AppData *data) { while (data->flash_addr - FLASH_START < BLOCK_SIZE) { // Read first portion of data from flash for FFT analysis - flash_read_bytes((uint8_t *)fft_buffer, data->flash_addr, FFT_SIZE * 2 * sizeof(int16_t)); + pbl_flash_read(FLASH, data->flash_addr, (uint8_t *)fft_buffer, FFT_SIZE * 2 * sizeof(int16_t)); data->flash_addr += FFT_SIZE * 2 * sizeof(int16_t); if (num_channels == 2) { // Convert interleaved stereo to non-interleaved format @@ -249,7 +249,7 @@ static void prv_mic_data_handler(int16_t *samples, size_t sample_count, void *co } // Write to flash - flash_write_bytes((uint8_t *)samples, data->flash_addr, sample_count * sizeof(int16_t)); + pbl_flash_write(FLASH, data->flash_addr, (uint8_t *)samples, sample_count * sizeof(int16_t)); data->flash_addr += sample_count * sizeof(int16_t); // Update progress @@ -266,7 +266,7 @@ static void prv_start_test(void) { data->flash_addr = FLASH_START; // Erase flash region for recording - flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); + pbl_flash_erase(FLASH, FLASH_START, FLASH_END - FLASH_START); uint32_t num_channels = mic_get_channels(MIC); diff --git a/src/fw/apps/prf/mfg_mic_obelix.c b/src/fw/apps/prf/mfg_mic_obelix.c index 2b23e90579..7d5fba55c1 100644 --- a/src/fw/apps/prf/mfg_mic_obelix.c +++ b/src/fw/apps/prf/mfg_mic_obelix.c @@ -73,7 +73,8 @@ static void prv_audio_trans_handler(uint32_t *free_size) { } mic_id = MIC1; while (available_size > PCM_BUFFER_SIZE*sizeof(int16_t)) { - flash_read_bytes((uint8_t *)app_data->pcm, app_data->flash_addr, PCM_BUFFER_SIZE*sizeof(int16_t)); + pbl_flash_read(FLASH, app_data->flash_addr, (uint8_t *)app_data->pcm, + PCM_BUFFER_SIZE * sizeof(int16_t)); app_data->flash_addr += PCM_BUFFER_SIZE*sizeof(int16_t); prv_interleaved_to_non_interleaved(app_data->pcm, PCM_BUFFER_SIZE/2); if (mic_id == MIC1) { @@ -116,14 +117,14 @@ static void prv_mic_data_handler(int16_t *samples, size_t sample_count, void *co prv_start_playback(); return; } - flash_write_bytes((uint8_t *)samples, app_data->flash_addr, sample_count*sizeof(int16_t)); + pbl_flash_write(FLASH, app_data->flash_addr, (uint8_t *)samples, sample_count*sizeof(int16_t)); app_data->flash_addr += sample_count*sizeof(int16_t); } static void prv_recording_start(void) { AppData *app_data = app_state_get_user_data(); app_data->flash_addr = FLASH_START; - flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); + pbl_flash_erase(FLASH, FLASH_START, FLASH_END - FLASH_START); // Disable back button during recording/playback window_set_overrides_back_button(&app_data->window, true); diff --git a/src/fw/apps/prf/mfg_test_result.c b/src/fw/apps/prf/mfg_test_result.c index fd67294721..ab9687c84a 100644 --- a/src/fw/apps/prf/mfg_test_result.c +++ b/src/fw/apps/prf/mfg_test_result.c @@ -44,15 +44,15 @@ static void prv_write_record(uint32_t index, MfgTestId test, uint8_t mode_index, .rsvd = 0, .value = value, }; - flash_write_bytes((const uint8_t *)&rec, - FLASH_REGION_MFG_RESULTS_BEGIN + index * sizeof(rec), sizeof(rec)); + pbl_flash_write(FLASH, FLASH_REGION_MFG_RESULTS_BEGIN + index * sizeof(rec), + (const uint8_t *)&rec, sizeof(rec)); } static void prv_load(void) { for (uint32_t i = 0; i < MFG_RESULTS_MAX_RECORDS; i++) { MfgResultRecord rec; - flash_read_bytes((uint8_t *)&rec, FLASH_REGION_MFG_RESULTS_BEGIN + i * sizeof(rec), - sizeof(rec)); + pbl_flash_read(FLASH, FLASH_REGION_MFG_RESULTS_BEGIN + i * sizeof(rec), (uint8_t *)&rec, + sizeof(rec)); if (rec.test_id == MFG_RESULT_EMPTY) { break; } @@ -78,7 +78,7 @@ static void prv_ensure_loaded(void) { static void prv_append(MfgTestId test, uint8_t mode_index, bool passed, uint32_t value) { if (s_record_count >= MFG_RESULTS_MAX_RECORDS) { // Log full: erase and rewrite the current state as a compacted log. - flash_erase_subsector_blocking(FLASH_REGION_MFG_RESULTS_BEGIN); + pbl_flash_erase(FLASH, FLASH_REGION_MFG_RESULTS_BEGIN, SUBSECTOR_SIZE_BYTES); s_record_count = 0; for (uint8_t m = 0; m < NUM_MODES; m++) { for (uint8_t t = 0; t < MfgTestIdCount; t++) { @@ -140,8 +140,8 @@ void mfg_test_result_reset(void) { s_result_reported = false; #ifdef CONFIG_MFG - if (!flash_subsector_is_erased(FLASH_REGION_MFG_RESULTS_BEGIN)) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_RESULTS_BEGIN); + if (!pbl_flash_is_erased(FLASH, FLASH_REGION_MFG_RESULTS_BEGIN, SUBSECTOR_SIZE_BYTES)) { + pbl_flash_erase(FLASH, FLASH_REGION_MFG_RESULTS_BEGIN, SUBSECTOR_SIZE_BYTES); } s_record_count = 0; s_loaded = true; diff --git a/src/fw/board/board_nrf5.h b/src/fw/board/board_nrf5.h index 192a9cdf8d..525396981f 100644 --- a/src/fw/board/board_nrf5.h +++ b/src/fw/board/board_nrf5.h @@ -202,8 +202,6 @@ typedef const struct I2CBus I2CBus; typedef const struct I2CSlavePort I2CSlavePort; typedef const struct HRMDevice HRMDevice; typedef const struct MicDevice MicDevice; -typedef const struct QSPIPort QSPIPort; -typedef const struct QSPIFlash QSPIFlash; typedef const struct AudioDevice AudioDevice; void board_early_init(void); diff --git a/src/fw/board/board_qemu.h b/src/fw/board/board_qemu.h index bae2fdb64a..ea41cbfb0e 100644 --- a/src/fw/board/board_qemu.h +++ b/src/fw/board/board_qemu.h @@ -153,8 +153,6 @@ typedef struct { typedef const struct UARTDevice UARTDevice; typedef const struct I2CBus I2CBus; typedef const struct I2CSlavePort I2CSlavePort; -typedef const struct QSPIPort QSPIPort; -typedef const struct QSPIFlash QSPIFlash; typedef const struct HRMDevice HRMDevice; typedef const struct MicDevice MicDevice; typedef const struct AudioDevice AudioDevice; diff --git a/src/fw/board/board_sf32lb52.h b/src/fw/board/board_sf32lb52.h index e5b73851e7..df3c52f539 100644 --- a/src/fw/board/board_sf32lb52.h +++ b/src/fw/board/board_sf32lb52.h @@ -139,8 +139,6 @@ typedef struct { const MagConfig mag_config; } BoardConfigMag; -#include -#include #include #include #include @@ -151,8 +149,6 @@ typedef const struct I2CBus I2CBus; typedef const struct I2CSlavePort I2CSlavePort; typedef const struct HRMDevice HRMDevice; typedef const struct MicDevice MicDevice; -typedef const struct QSPIPort QSPIPort; -typedef const struct QSPIFlash QSPIFlash; typedef const struct DisplayJDIDevice DisplayJDIDevice; typedef const struct AudioDevice AudioDevice; diff --git a/src/fw/board/boards/board_asterix.c b/src/fw/board/boards/board_asterix.c index e7a67371ef..853230d030 100644 --- a/src/fw/board/boards/board_asterix.c +++ b/src/fw/board/boards/board_asterix.c @@ -4,8 +4,9 @@ #include #include "board/board.h" +#include "flash_region/flash_region.h" #include -#include +#include #include #include #include @@ -14,7 +15,6 @@ #include #include #include -#include #include #include "flash_region/flash_region.h" @@ -25,9 +25,20 @@ #include #include -static QSPIPortState s_qspi_port_state; -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, +static struct pbl_flash_nrf5_qspi_state s_flash_state; +static const struct pbl_flash_nrf5_qspi s_flash = { + .dev = + { + .state = &s_flash_state.flash, + .ops = &pbl_flash_nrf5_qspi_ops, + .size = BOARD_NOR_FLASH_SIZE, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, + .sector_erase_ms = 150, + .subsector_erase_ms = 50, + .sec_regs = &pbl_flash_nor_gd25lq255e.sec_regs, + }, + .part = &pbl_flash_nor_gd25lq255e, .clk_freq_hz = 8000000UL, .cs_gpio = NRF_GPIO_PIN_MAP(0, 17), .clk_gpio = NRF_GPIO_PIN_MAP(0, 19), @@ -38,17 +49,10 @@ static QSPIPort QSPI_PORT = { NRF_GPIO_PIN_MAP(0, 22), NRF_GPIO_PIN_MAP(0, 23), }, + .read_mode = PBL_FLASH_NRF5_QSPI_READ_READ4IO, + .write_mode = PBL_FLASH_NRF5_QSPI_WRITE_PP4O, }; -QSPIPort *const QSPI = &QSPI_PORT; - -static QSPIFlashState s_qspi_flash_state; -static QSPIFlash QSPI_FLASH_DEVICE = { - .state = &s_qspi_flash_state, - .qspi = &QSPI_PORT, - .read_mode = QSPI_FLASH_READ_READ4IO, - .write_mode = QSPI_FLASH_WRITE_PP4O, -}; -QSPIFlash *const QSPI_FLASH = &QSPI_FLASH_DEVICE; +const struct pbl_flash_device *const FLASH = &s_flash.dev; /* PERIPHERAL ID 43 */ static UARTDeviceState s_dbg_uart_state; diff --git a/src/fw/board/boards/board_asterix.h b/src/fw/board/boards/board_asterix.h index 4590775673..a5cae26018 100644 --- a/src/fw/board/boards/board_asterix.h +++ b/src/fw/board/boards/board_asterix.h @@ -99,8 +99,6 @@ static const BoardConfigSharpDisplay BOARD_CONFIG_DISPLAY = { }, }; -extern QSPIPort * const QSPI; -extern QSPIFlash * const QSPI_FLASH; extern MicDevice * const MIC; extern AudioDevice * const AUDIO; diff --git a/src/fw/board/boards/board_getafix.c b/src/fw/board/boards/board_getafix.c index 8777375ee2..b8b4b3136e 100644 --- a/src/fw/board/boards/board_getafix.c +++ b/src/fw/board/boards/board_getafix.c @@ -2,6 +2,9 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" +#include "flash_region/flash_region.h" +#include +#include #include "board/splash.h" #include #include "system/passert.h" @@ -75,7 +78,7 @@ static UARTDevice HCI_TRACE_UART_DEVICE = { UARTDevice *const HCI_TRACE_UART = &HCI_TRACE_UART_DEVICE; #endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY -static QSPIPortState s_qspi_port_state = { +static struct pbl_flash_sf32lb52_mpi_state s_flash_state = { .cfg = { .Instance = FLASH2, .line = HAL_FLASH_QMODE, @@ -88,22 +91,25 @@ static QSPIPortState s_qspi_port_state = { .dma_irq = DMAC1_CH2_IRQn, .request = DMA_REQUEST_1, }, - .t_enter_deep_us = 3, - .t_exit_deep_us = 20, }; - -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, +static const struct pbl_flash_sf32lb52_mpi s_flash = { + .dev = + { + .state = &s_flash_state.flash, + .ops = &pbl_flash_sf32lb52_mpi_ops, + .base = FLASH_REGION_BASE_ADDRESS, + .size = BOARD_NOR_FLASH_SIZE, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, + .sec_regs = &pbl_flash_nor_gd25q256e.sec_regs, + }, + .name = "GD25Q256E", + .id = 0x1940c8, .clk_div = 0U, + .dpd_enter_us = 3, + .dpd_exit_us = 20, }; -QSPIPort *const QSPI = &QSPI_PORT; - -static QSPIFlashState s_qspi_flash_state; -static QSPIFlash QSPI_FLASH_DEVICE = { - .state = &s_qspi_flash_state, - .qspi = &QSPI_PORT, -}; -QSPIFlash *const QSPI_FLASH = &QSPI_FLASH_DEVICE; +const struct pbl_flash_device *const FLASH = &s_flash.dev; static DisplayJDIState s_display_state = { .hlcdc = { diff --git a/src/fw/board/boards/board_getafix.h b/src/fw/board/boards/board_getafix.h index 621ea761c1..c7230c4427 100644 --- a/src/fw/board/boards/board_getafix.h +++ b/src/fw/board/boards/board_getafix.h @@ -17,8 +17,6 @@ extern UARTDevice * const DBG_UART; #ifdef NIMBLE_HCI_SF32LB52_TRACE_BINARY extern UARTDevice * const HCI_TRACE_UART; #endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY -extern QSPIPort * const QSPI; -extern QSPIFlash * const QSPI_FLASH; extern I2CBus *const I2C1_BUS; extern I2CBus *const I2C2_BUS; extern I2CBus *const I2C3_BUS; diff --git a/src/fw/board/boards/board_obelix.c b/src/fw/board/boards/board_obelix.c index db45cf5514..9256d04609 100644 --- a/src/fw/board/boards/board_obelix.c +++ b/src/fw/board/boards/board_obelix.c @@ -2,6 +2,9 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" +#include "flash_region/flash_region.h" +#include +#include #include "board/splash.h" #include #include @@ -194,7 +197,7 @@ static UARTDevice HCI_TRACE_UART_DEVICE = { UARTDevice *const HCI_TRACE_UART = &HCI_TRACE_UART_DEVICE; #endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY -static QSPIPortState s_qspi_port_state = { +static struct pbl_flash_sf32lb52_mpi_state s_flash_state = { .cfg = { .Instance = FLASH2, .line = HAL_FLASH_QMODE, @@ -207,22 +210,25 @@ static QSPIPortState s_qspi_port_state = { .dma_irq = DMAC1_CH2_IRQn, .request = DMA_REQUEST_1, }, - .t_enter_deep_us = 3, - .t_exit_deep_us = 20, }; - -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, +static const struct pbl_flash_sf32lb52_mpi s_flash = { + .dev = + { + .state = &s_flash_state.flash, + .ops = &pbl_flash_sf32lb52_mpi_ops, + .base = FLASH_REGION_BASE_ADDRESS, + .size = BOARD_NOR_FLASH_SIZE, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, + .sec_regs = &pbl_flash_nor_gd25q256e.sec_regs, + }, + .name = "GD25Q256E", + .id = 0x1940c8, .clk_div = 0U, + .dpd_enter_us = 3, + .dpd_exit_us = 20, }; -QSPIPort *const QSPI = &QSPI_PORT; - -static QSPIFlashState s_qspi_flash_state; -static QSPIFlash QSPI_FLASH_DEVICE = { - .state = &s_qspi_flash_state, - .qspi = &QSPI_PORT, -}; -QSPIFlash *const QSPI_FLASH = &QSPI_FLASH_DEVICE; +const struct pbl_flash_device *const FLASH = &s_flash.dev; static I2CBusHalState s_i2c_bus_hal_state_1 = { .hdl = { diff --git a/src/fw/board/boards/board_obelix.h b/src/fw/board/boards/board_obelix.h index 8b230a1bc5..a8f9083886 100644 --- a/src/fw/board/boards/board_obelix.h +++ b/src/fw/board/boards/board_obelix.h @@ -16,8 +16,6 @@ extern UARTDevice * const DBG_UART; #ifdef NIMBLE_HCI_SF32LB52_TRACE_BINARY extern UARTDevice * const HCI_TRACE_UART; #endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY -extern QSPIPort * const QSPI; -extern QSPIFlash * const QSPI_FLASH; extern I2CBus *const I2C1_BUS; extern I2CBus *const I2C2_BUS; extern const LSM6DSOConfig *const LSM6DSO; diff --git a/src/fw/board/boards/board_qemu_emery.c b/src/fw/board/boards/board_qemu_emery.c index 28b91a4cb7..490837e21d 100644 --- a/src/fw/board/boards/board_qemu_emery.c +++ b/src/fw/board/boards/board_qemu_emery.c @@ -2,6 +2,8 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" +#include "flash_region/flash_region.h" +#include // UART device for debug serial #include @@ -33,6 +35,21 @@ static struct UARTDevice QEMU_UART_DEVICE = { UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; +static struct pbl_flash_device_state s_flash_state; +static const struct pbl_flash_qemu s_flash = { + .dev = + { + .state = &s_flash_state, + .ops = &pbl_flash_qemu_ops, + .base = FLASH_REGION_BASE_ADDRESS, + .size = BOARD_NOR_FLASH_SIZE, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, + }, + .regs = QEMU_EXTFLASH_BASE, +}; +const struct pbl_flash_device *const FLASH = &s_flash.dev; + // Display device - QEMU framebuffer at DISPLAY_BASE + 0x1000 static QemuDisplayDevice s_display = { .base_addr = QEMU_DISPLAY_BASE, diff --git a/src/fw/board/boards/board_qemu_flint.c b/src/fw/board/boards/board_qemu_flint.c index 24a1ad8830..5256fb0d4a 100644 --- a/src/fw/board/boards/board_qemu_flint.c +++ b/src/fw/board/boards/board_qemu_flint.c @@ -2,6 +2,8 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" +#include "flash_region/flash_region.h" +#include // UART device for debug serial #include @@ -31,6 +33,21 @@ static struct UARTDevice QEMU_UART_DEVICE = { UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; +static struct pbl_flash_device_state s_flash_state; +static const struct pbl_flash_qemu s_flash = { + .dev = + { + .state = &s_flash_state, + .ops = &pbl_flash_qemu_ops, + .base = FLASH_REGION_BASE_ADDRESS, + .size = BOARD_NOR_FLASH_SIZE, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, + }, + .regs = QEMU_EXTFLASH_BASE, +}; +const struct pbl_flash_device *const FLASH = &s_flash.dev; + // Display device - QEMU framebuffer static QemuDisplayDevice s_display = { .base_addr = QEMU_DISPLAY_BASE, diff --git a/src/fw/board/boards/board_qemu_gabbro.c b/src/fw/board/boards/board_qemu_gabbro.c index afcc2c2707..9c93ce67ca 100644 --- a/src/fw/board/boards/board_qemu_gabbro.c +++ b/src/fw/board/boards/board_qemu_gabbro.c @@ -2,6 +2,8 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" +#include "flash_region/flash_region.h" +#include // UART device for debug serial #include @@ -30,6 +32,21 @@ static struct UARTDevice QEMU_UART_DEVICE = { UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; +static struct pbl_flash_device_state s_flash_state; +static const struct pbl_flash_qemu s_flash = { + .dev = + { + .state = &s_flash_state, + .ops = &pbl_flash_qemu_ops, + .base = FLASH_REGION_BASE_ADDRESS, + .size = BOARD_NOR_FLASH_SIZE, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, + }, + .regs = QEMU_EXTFLASH_BASE, +}; +const struct pbl_flash_device *const FLASH = &s_flash.dev; + // Display device - QEMU framebuffer static QemuDisplayDevice s_display = { .base_addr = QEMU_DISPLAY_BASE, diff --git a/src/fw/console/prompt_commands.c b/src/fw/console/prompt_commands.c index e4e63f86d3..84f998ef64 100644 --- a/src/fw/console/prompt_commands.c +++ b/src/fw/console/prompt_commands.c @@ -12,6 +12,8 @@ #include "dbgserial.h" #include "debug/flash_logging.h" #include +#include +#include "system/status_codes.h" #include #include "flash_region/flash_region.h" #include "kernel/event_loop.h" @@ -89,11 +91,7 @@ void command_erase_flash(const char *address_str, const char *length_str) { prompt_send_response_fmt(buffer, 128, "Erasing sectors from 0x%"PRIx32" for %ub", address, length); - const uint32_t end_address = address + length; - const uint32_t aligned_end_address = - (end_address + (SUBSECTOR_SIZE_BYTES - 1)) & SUBSECTOR_ADDR_MASK; - - flash_region_erase_optimal_range_no_watchdog(address, address, end_address, aligned_end_address); + pbl_flash_erase(FLASH, address, length); prompt_send_response("OK"); } @@ -118,7 +116,7 @@ void command_dump_flash(const char* address_str, const char* length_str) { while (length) { uint32_t chunk_size = MIN(length, 128); - flash_read_bytes(buffer, address, chunk_size); + pbl_flash_read(FLASH, address, buffer, chunk_size); PBL_LOG_ALWAYS("Data at address 0x%"PRIx32, address); hexdump_log(LOG_LEVEL_ALWAYS, buffer, chunk_size); @@ -144,7 +142,7 @@ void command_crc_flash(const char* address_str, const char* length_str) { return; } - uint32_t crc = flash_calculate_legacy_defective_checksum(address, length); + uint32_t crc = pbl_flash_legacy_checksum(FLASH, address, length); char buffer[32]; prompt_send_response_fmt(buffer, sizeof(buffer), "CRC: %"PRIx32, crc); } @@ -178,7 +176,7 @@ void command_flash_read(const char* address_str, const char* length_str) { read_length = length; } - flash_read_bytes(buffer, address, read_length); + pbl_flash_read(FLASH, address, buffer, read_length); // Output to serial for (uint32_t i = 0; i < read_length; i++) { @@ -226,12 +224,17 @@ void command_flash_fill (const char* address_str, const char* length_str, const bytes_to_write = bytes_remaining; } - flash_write_bytes(page, address, bytes_to_write); + pbl_flash_write(FLASH, address, page, bytes_to_write); bytes_remaining -= bytes_to_write; address += bytes_to_write; } } +void command_flash_unprotect(void) { + pbl_flash_unprotect(FLASH); + prompt_send_response("OK"); +} + void command_flash_validate(void) { // just test one sector, which is probably less than the size of the region const uint32_t TEST_ADDR = FLASH_REGION_FIRMWARE_DEST_BEGIN; @@ -240,8 +243,8 @@ void command_flash_validate(void) { PBL_ASSERTN((TEST_ADDR + TEST_LENGTH) <= FLASH_REGION_FIRMWARE_DEST_END); // erase a sector - flash_erase_sector_blocking(TEST_ADDR); - if (!flash_sector_is_erased(TEST_ADDR)) { + pbl_flash_erase(FLASH, TEST_ADDR, SECTOR_SIZE_BYTES); + if (!pbl_flash_is_erased(FLASH, TEST_ADDR, SECTOR_SIZE_BYTES)) { prompt_send_response("FAIL: sector not erased"); return; } @@ -254,14 +257,14 @@ void command_flash_validate(void) { } for (uint32_t offset = 0; offset < TEST_LENGTH; offset += BUFFER_SIZE) { const uint32_t addr = TEST_ADDR + offset; - flash_write_bytes(buffer, addr, BUFFER_SIZE); + pbl_flash_write(FLASH, addr, buffer, BUFFER_SIZE); } // read it back for (uint32_t offset = 0; offset < TEST_LENGTH; offset += BUFFER_SIZE) { memset(buffer, 0, BUFFER_SIZE); const uint32_t addr = TEST_ADDR + offset; - flash_read_bytes(buffer, addr, BUFFER_SIZE); + pbl_flash_read(FLASH, addr, buffer, BUFFER_SIZE); for (uint32_t i = 0; i < BUFFER_SIZE; i++) { if (buffer[i] != i) { char err_buf[80]; @@ -281,11 +284,11 @@ void command_flash_validate(void) { const uint32_t pre_addr = TEST_ADDR + offset - MIN(offset, 1); uint8_t pre_byte; - flash_read_bytes(&pre_byte, pre_addr, sizeof(pre_byte)); + pbl_flash_read(FLASH, pre_addr, &pre_byte, sizeof(pre_byte)); const uint32_t addr = TEST_ADDR + offset; size_t read_size = MIN(sizeof(memmap_buffer), SHORT_TEST_LENGTH - offset); - flash_read_bytes(&memmap_buffer[0], addr, read_size); + pbl_flash_read(FLASH, addr, &memmap_buffer[0], read_size); for (size_t i = 0; i < read_size; i++) { uint8_t want = (offset + i) & 0xff; if (memmap_buffer[i] != want) { @@ -298,8 +301,8 @@ void command_flash_validate(void) { } // clean up - flash_erase_sector_blocking(TEST_ADDR); - if (!flash_sector_is_erased(TEST_ADDR)) { + pbl_flash_erase(FLASH, TEST_ADDR, SECTOR_SIZE_BYTES); + if (!pbl_flash_is_erased(FLASH, TEST_ADDR, SECTOR_SIZE_BYTES)) { prompt_send_response("FAIL: sector not erased"); return; } @@ -310,12 +313,13 @@ void command_flash_validate(void) { //! Some flash chips have an accelerated method of checking for erased sectors. This is a sanity //! check against that method. It reads the bytes in raw form and makes sure it is really erased. static bool prv_is_really_erased(uint32_t addr, bool is_subsector) { - bool erased = (is_subsector) ? flash_subsector_is_erased(addr) : flash_sector_is_erased(addr); + bool erased = (is_subsector) ? pbl_flash_is_erased(FLASH, addr, SUBSECTOR_SIZE_BYTES) + : pbl_flash_is_erased(FLASH, addr, SECTOR_SIZE_BYTES); if (erased) { char buffer[64]; uint32_t end_addr = addr + (is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES); for (uint32_t i_addr = addr; i_addr < end_addr; i_addr += sizeof(buffer)) { - flash_read_bytes((uint8_t *)buffer, i_addr, sizeof(buffer)); + pbl_flash_read(FLASH, i_addr, (uint8_t *)buffer, sizeof(buffer)); for (uint32_t j = 0; j < sizeof(buffer); j++) { if (buffer[j] != 0xFF) { erased = false; @@ -364,8 +368,8 @@ void command_flash_sec_read(const char *address_str) { status_t ret; char buf[64]; - ret = flash_read_security_register(address, &val); - if (ret != S_SUCCESS) { + ret = pbl_flash_sec_reg_read(FLASH, address, &val); + if (ret != 0) { prompt_send_response("FAIL: Unable to read security register"); return; } @@ -378,8 +382,8 @@ void command_flash_sec_write(const char *address_str, const char *value_str) { uint8_t value = (uint8_t)strtoul(value_str, NULL, 0); status_t ret; - ret = flash_write_security_register(address, value); - if (ret != S_SUCCESS) { + ret = pbl_flash_sec_reg_write(FLASH, address, value); + if (ret != 0) { prompt_send_response("FAIL: Unable to write security register"); return; } @@ -391,8 +395,8 @@ void command_flash_sec_erase(const char *address_str) { uint32_t address = strtoul(address_str, NULL, 0); status_t ret; - ret = flash_erase_security_register(address); - if (ret != S_SUCCESS) { + ret = pbl_flash_sec_reg_erase(FLASH, address); + if (ret != 0) { prompt_send_response("FAIL: Unable to erase security register"); return; } @@ -401,12 +405,12 @@ void command_flash_sec_erase(const char *address_str) { } void command_flash_sec_wipe(void) { - const FlashSecurityRegisters *info = flash_security_registers_info(); + const struct pbl_flash_sec_regs *info = FLASH->sec_regs; status_t ret; - for (uint8_t i = 0U; i < info->num_sec_regs; i++) { - ret = flash_erase_security_register(info->sec_regs[i]); - if (ret != S_SUCCESS) { + for (uint8_t i = 0U; info != NULL && i < info->count; i++) { + ret = pbl_flash_sec_reg_erase(FLASH, info->addrs[i]); + if (ret != 0) { prompt_send_response("FAIL: Unable to erase security register"); return; } @@ -416,27 +420,27 @@ void command_flash_sec_wipe(void) { } void command_flash_sec_info(void) { - const FlashSecurityRegisters *info = flash_security_registers_info(); + const struct pbl_flash_sec_regs *info = FLASH->sec_regs; char buf[64]; - if (info->sec_regs == NULL) { + if (info == NULL) { prompt_send_response("No security registers"); return; } - prompt_send_response_fmt(buf, sizeof(buf), "Number of security registers: %d", info->num_sec_regs); - for (int i = 0; i < info->num_sec_regs; i++) { + prompt_send_response_fmt(buf, sizeof(buf), "Number of security registers: %d", info->count); + for (int i = 0; i < info->count; i++) { bool locked; status_t ret; - ret = flash_security_register_is_locked(info->sec_regs[i], &locked); - if (ret != S_SUCCESS) { + ret = pbl_flash_sec_reg_is_locked(FLASH, info->addrs[i], &locked); + if (ret != 0) { prompt_send_response("FAIL: Unable to check security register lock status"); return; } prompt_send_response_fmt(buf, sizeof(buf), "Security register %d: 0x%08lx (locked: %u)", - i, info->sec_regs[i], locked); + i, info->addrs[i], locked); } } @@ -444,7 +448,7 @@ void command_flash_sec_info(void) { void command_flash_sec_lock(const char *address_str, const char *password) { if (strcmp(password, "l0ckm3f0r3v3r") == 0) { uint32_t address = strtoul(address_str, NULL, 0); - flash_lock_security_register(address); + pbl_flash_sec_reg_lock(FLASH, address); prompt_send_response("OK"); } else { prompt_send_response("FAIL: Invalid password"); @@ -493,11 +497,12 @@ static void prv_flash_stress_callback(void *data) { } int miscompare = 0; - - uint32_t sector_address = flash_get_sector_base_address(flash_addr + bufsz); // the beginning has already been erased, since we are always smaller than a sector + + // the beginning has already been erased, since we are always smaller than a sector + uint32_t sector_address = (flash_addr + bufsz) & SECTOR_ADDR_MASK; if (sector_address != s_flash_stress_last_sector) { PBL_LOG_ALWAYS("flash stress test: erasing flash address %lx", sector_address); - flash_erase_sector_blocking(sector_address); + pbl_flash_erase(FLASH, sector_address, SECTOR_SIZE_BYTES); s_flash_stress_last_sector = sector_address; if (!prv_is_really_erased(sector_address, 0)) { PBL_LOG_ALWAYS("flash stress test: flash address %lx erase failed!", sector_address); @@ -512,11 +517,11 @@ static void prv_flash_stress_callback(void *data) { lfsr_cur = prv_xorshift32(lfsr_cur); } - flash_write_bytes((const uint8_t *)buf, flash_addr, bufsz); + pbl_flash_write(FLASH, flash_addr, (const uint8_t *)buf, bufsz); for (int j = 0; j < 8; j++) { memset(buf, 0, bufsz); - flash_read_bytes(buf, flash_addr, bufsz); + pbl_flash_read(FLASH, flash_addr, buf, bufsz); lfsr_cur = lfsr_seed; @@ -571,7 +576,7 @@ static void s_flash_benchmark(size_t sz) { iters *= 2; for (int i = 0; i < iters; i++) { - flash_read_bytes(buf, flash_addr, sz); + pbl_flash_read(FLASH, flash_addr, buf, sz); flash_addr += sz; flash_addr &= ~3; /* keep us aligned */ flash_addr &= ~(SUBSECTOR_SIZE_BYTES << 1); /* keep us from wrapping too far */ @@ -913,11 +918,11 @@ void command_flash_test_locked_sectors(void) { if ((addr % SECTOR_SIZE_BYTES) == 0) { prompt_send_response_fmt(status, sizeof(status), "Validated: 0x%lx", addr); - flash_erase_sector_blocking(addr); - flash_erase_sector_blocking(addr); // exercise already erased check + pbl_flash_erase(FLASH, addr, SECTOR_SIZE_BYTES); + pbl_flash_erase(FLASH, addr, SECTOR_SIZE_BYTES); // exercise already erased check } - flash_write_bytes(&buf[0], addr, sizeof(buf)); + pbl_flash_write(FLASH, addr, &buf[0], sizeof(buf)); flash_expect_program_failure(false); watchdog_feed(); diff --git a/src/fw/console/pulse_bulkio_coredump.c b/src/fw/console/pulse_bulkio_coredump.c index 500d9c4e78..2f8bd843fd 100644 --- a/src/fw/console/pulse_bulkio_coredump.c +++ b/src/fw/console/pulse_bulkio_coredump.c @@ -25,7 +25,7 @@ static int coredump_domain_read(uint8_t *buf, uint32_t address, uint32_t length, void *context) { uint32_t index = (uintptr_t)context; uint32_t core_base_addr = core_dump_get_slot_address(index) + sizeof(CoreDumpFlashRegionHeader); - flash_read_bytes(buf, core_base_addr + address, length); + pbl_flash_read(FLASH, core_base_addr + address, buf, length); return length; } diff --git a/src/fw/console/pulse_bulkio_external_flash.c b/src/fw/console/pulse_bulkio_external_flash.c index 2221455d8d..7402497795 100644 --- a/src/fw/console/pulse_bulkio_external_flash.c +++ b/src/fw/console/pulse_bulkio_external_flash.c @@ -25,13 +25,13 @@ typedef struct ExternalFlashEraseState { static int external_flash_domain_read(uint8_t *buf, uint32_t address, uint32_t length, void *context) { - flash_read_bytes(buf, address, length); + pbl_flash_read(FLASH, address, buf, length); return length; } static int external_flash_domain_write(uint8_t *buf, uint32_t address, uint32_t length, void *context) { - flash_write_bytes(buf, address, length); + pbl_flash_write(FLASH, address, buf, length); return length; } @@ -39,20 +39,20 @@ static int external_flash_domain_stat(uint8_t *resp, size_t resp_max_len, void * return E_INVALID_OPERATION; } -static void prv_erase_sector(void *context, status_t result) { +static void prv_erase_sector(void *context, int result) { ExternalFlashEraseState *state = context; const unsigned int sectors_to_erase = ( state->length + SECTOR_SIZE_BYTES - 1) / SECTOR_SIZE_BYTES; - if (FAILED(result)) { + if (result != 0) { pulse_bulkio_erase_message_send(PulseBulkIODomainType_ExternalFlash, result, state->cookie); kernel_free(state); } else if (state->next_sector < sectors_to_erase) { unsigned int sector_addr = state->address + state->next_sector * SECTOR_SIZE_BYTES; state->next_sector += 1; pulse_bulkio_erase_message_send(PulseBulkIODomainType_ExternalFlash, S_TRUE, state->cookie); - flash_erase_sector(sector_addr, prv_erase_sector, state); + pbl_flash_erase_async(FLASH, sector_addr, SECTOR_SIZE_BYTES, prv_erase_sector, state); } else { pulse_bulkio_erase_message_send(PulseBulkIODomainType_ExternalFlash, S_SUCCESS, state->cookie); kernel_free(state); diff --git a/src/fw/console/pulse_flash_imaging.c b/src/fw/console/pulse_flash_imaging.c index f3d28bc71e..0dfca22044 100644 --- a/src/fw/console/pulse_flash_imaging.c +++ b/src/fw/console/pulse_flash_imaging.c @@ -106,7 +106,7 @@ typedef struct PACKED EraseAndWriteAck { uint8_t complete; } EraseAndWriteAck; -static void prv_erase_complete(void *ignored, status_t result); +static void prv_erase_complete(void *ignored, int result); static void prv_handle_erase(Command *cmd, size_t length) { if (length != sizeof(cmd->erase)) { @@ -139,15 +139,11 @@ static void prv_handle_erase(Command *cmd, size_t length) { }; pulse_best_effort_send(message, sizeof(EraseAndWriteAck)); - uint32_t end_address = cmd->erase.address + cmd->erase.length; - flash_erase_optimal_range( - cmd->erase.address, cmd->erase.address, end_address, - (end_address + SECTOR_SIZE_BYTES - 1) & SECTOR_ADDR_MASK, - prv_erase_complete, NULL); + pbl_flash_erase_async(FLASH, cmd->erase.address, cmd->erase.length, prv_erase_complete, NULL); } } -static void prv_erase_complete(void *ignored, status_t result) { +static void prv_erase_complete(void *ignored, int result) { EraseAndWriteAck *message = pulse_best_effort_send_begin( PULSE_PROTOCOL_FLASH_IMAGING); *message = (EraseAndWriteAck) { @@ -157,7 +153,7 @@ static void prv_erase_complete(void *ignored, status_t result) { .complete = 1 }; - if (FAILED(result)) { + if (result != 0) { message->opcode = IMAGING_RESP_INTERNAL_ERROR; pulse_best_effort_send(message, sizeof(message->opcode)); } else { @@ -174,7 +170,7 @@ static void prv_handle_write(Command *cmd, size_t command_length) { } size_t write_length = command_length - sizeof(cmd->write); - flash_write_bytes(&cmd->write.data[0], cmd->write.address, write_length); + pbl_flash_write(FLASH, cmd->write.address, &cmd->write.data[0], write_length); EraseAndWriteAck *ack = pulse_best_effort_send_begin( PULSE_PROTOCOL_FLASH_IMAGING); @@ -206,7 +202,7 @@ static void prv_handle_crc(Command *cmd, size_t length) { uint32_t crc; } CrcAck; - uint32_t crc = flash_calculate_legacy_defective_checksum(cmd->crc.address, cmd->crc.length); + uint32_t crc = pbl_flash_legacy_checksum(FLASH, cmd->crc.address, cmd->crc.length); CrcAck *ack = pulse_best_effort_send_begin(PULSE_PROTOCOL_FLASH_IMAGING); *ack = (CrcAck) { @@ -228,7 +224,7 @@ static void prv_handle_query_region(Command *cmd, size_t length) { switch (cmd->region.region) { case FLASH_REGION_PRF: // assume a query of the region means we are going to write to it - flash_prf_set_protection(false); + pbl_flash_unprotect(FLASH); region_base = FLASH_REGION_SAFE_FIRMWARE_BEGIN; region_length = FLASH_REGION_SAFE_FIRMWARE_END - FLASH_REGION_SAFE_FIRMWARE_BEGIN; @@ -272,7 +268,8 @@ static void prv_handle_finalize_region(Command *cmd, size_t length) { switch (cmd->region.region) { case FLASH_REGION_PRF: - flash_prf_set_protection(true); + pbl_flash_protect(FLASH, FLASH_REGION_SAFE_FIRMWARE_BEGIN, + FLASH_REGION_SAFE_FIRMWARE_END - FLASH_REGION_SAFE_FIRMWARE_BEGIN); break; case FLASH_REGION_SYSTEM_RESOURCES: boot_bit_set(BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE); diff --git a/src/fw/debug/flash_logging.c b/src/fw/debug/flash_logging.c index f07ecd22e5..3b9a2d084c 100644 --- a/src/fw/debug/flash_logging.c +++ b/src/fw/debug/flash_logging.c @@ -4,6 +4,7 @@ #include "debug/flash_logging.h" #include +#include #include "flash_region/flash_region.h" #include "kernel/pbl_malloc.h" #include "pbl/services/system_task.h" @@ -152,7 +153,7 @@ static uint8_t prv_get_next_log_file_id(uint8_t file_id) { static uint32_t prv_get_unit_base_address(uint32_t addr) { #if defined(CONFIG_BOARD_ASTERIX) || defined(CONFIG_BOARD_OBELIX) || defined(CONFIG_BOARD_GETAFIX) || defined(CONFIG_BOARD_QEMU_EMERY) || defined(CONFIG_BOARD_QEMU_FLINT) || defined(CONFIG_BOARD_QEMU_GABBRO) - return flash_get_subsector_base_address(addr); + return (addr & SUBSECTOR_ADDR_MASK); #else #error "Invalid platform!" #endif @@ -160,7 +161,7 @@ static uint32_t prv_get_unit_base_address(uint32_t addr) { static void prv_erase_unit(uint32_t addr) { #if defined(CONFIG_BOARD_ASTERIX) || defined(CONFIG_BOARD_OBELIX) || defined(CONFIG_BOARD_GETAFIX) || defined(CONFIG_BOARD_QEMU_EMERY) || defined(CONFIG_BOARD_QEMU_FLINT) || defined(CONFIG_BOARD_QEMU_GABBRO) - flash_erase_subsector_blocking(addr); + pbl_flash_erase(FLASH, addr, SUBSECTOR_SIZE_BYTES); #else #error "Invalid platform!" #endif @@ -196,7 +197,7 @@ static uint32_t prv_validate_flash_log_region(uint8_t *first_log_file_id) { uint32_t flash_addr = FLASH_REGION_DEBUG_DB_BEGIN + offset; FlashLoggingHeader hdr; - flash_read_bytes((uint8_t *)&hdr, flash_addr, sizeof(hdr)); + pbl_flash_read(FLASH, flash_addr, (uint8_t *)&hdr, sizeof(hdr)); if (!prv_flash_log_valid(&hdr)) { // is the region erased ? FlashLoggingHeader erased_hdr; @@ -228,7 +229,7 @@ static int prv_get_start_of_log_file(uint8_t log_file_id, uint32_t flash_addr = FLASH_REGION_DEBUG_DB_BEGIN + offset; FlashLoggingHeader hdr; - flash_read_bytes((uint8_t *)&hdr, flash_addr, sizeof(hdr)); + pbl_flash_read(FLASH, flash_addr, (uint8_t *)&hdr, sizeof(hdr)); bool in_use_and_valid = prv_flash_log_valid(&hdr); @@ -280,7 +281,7 @@ static void prv_allocate_page_for_use(void) { const uint8_t *build_id = version_get_build_id(&len); memcpy(hdr.build_id, build_id, sizeof(hdr.build_id)); - flash_write_bytes((uint8_t *)&hdr, s_curr_state.page_start_addr, sizeof(hdr)); + pbl_flash_write(FLASH, s_curr_state.page_start_addr, (uint8_t *)&hdr, sizeof(hdr)); s_curr_state.offset_in_log_page = sizeof(hdr); } @@ -307,7 +308,7 @@ void flash_logging_init(void) { prv_get_page_addr(FLASH_REGION_DEBUG_DB_BEGIN + first_used_region, offset); FlashLoggingHeader hdr; - flash_read_bytes((uint8_t *)&hdr, flash_addr, sizeof(hdr)); + pbl_flash_read(FLASH, flash_addr, (uint8_t *)&hdr, sizeof(hdr)); if (prv_flash_log_valid(&hdr)) { // we use serial distance to find the gap in the numbering @@ -360,7 +361,7 @@ static void prv_write_flash_log_record_header(uint8_t msg_length) { record_hdr.length = msg_length; uint32_t addr = s_curr_state.page_start_addr + s_curr_state.offset_in_log_page; - flash_write_bytes((uint8_t *)&record_hdr, addr, sizeof(record_hdr)); + pbl_flash_write(FLASH, addr, (uint8_t *)&record_hdr, sizeof(record_hdr)); s_curr_state.offset_in_log_page += sizeof(record_hdr); } @@ -409,7 +410,7 @@ bool flash_logging_write(const uint8_t *data_to_write, uint32_t flash_addr, } uint32_t addr = s_curr_state.page_start_addr + s_curr_state.offset_in_log_page; - flash_write_bytes(data_to_write, addr, read_length); + pbl_flash_write(FLASH, addr, data_to_write, read_length); s_curr_state.offset_in_log_page += read_length; s_curr_state.bytes_remaining -= read_length; @@ -417,8 +418,7 @@ bool flash_logging_write(const uint8_t *data_to_write, uint32_t flash_addr, if (s_curr_state.bytes_remaining == 0) { // we are done with the current log record, mark it valid uint8_t flags = ~(LOG_FLAGS_VALID); - flash_write_bytes((uint8_t *)&flags, s_curr_state.log_start_addr, - sizeof(flags)); + pbl_flash_write(FLASH, s_curr_state.log_start_addr, (uint8_t *)&flags, sizeof(flags)); } return (true); @@ -443,7 +443,7 @@ static void prv_dump_log_system_cb(void *context) { uint8_t build_id[MEMBER_SIZE(FlashLoggingHeader, build_id)]; uint32_t build_id_addr = flash_addr + offsetof(FlashLoggingHeader, build_id); - flash_read_bytes((uint8_t *)build_id, build_id_addr, sizeof(build_id)); + pbl_flash_read(FLASH, build_id_addr, (uint8_t *)build_id, sizeof(build_id)); byte_stream_to_hex_string((char *)&state->msg_buf[off], MAX_MSG_LEN - off, (uint8_t *)build_id, sizeof(build_id), false); int len = pbl_log_get_bin_format((char *)state->msg_buf, MAX_MSG_LEN, LOG_LEVEL_INFO, "", 0, @@ -466,7 +466,7 @@ static void prv_dump_log_system_cb(void *context) { // Read next log message and send it out LogRecordHeader rec; - flash_read_bytes((uint8_t *)&rec, flash_addr + state->page_offset, sizeof(rec)); + pbl_flash_read(FLASH, flash_addr + state->page_offset, (uint8_t *)&rec, sizeof(rec)); bool page_done = false; if ((rec.length > MAX_MSG_LEN) || (rec.length == 0)) { // The record contents indicate the end of a page @@ -476,7 +476,8 @@ static void prv_dump_log_system_cb(void *context) { // This record has data, read it out if ((~rec.flags & LOG_FLAGS_VALID) != 0) { // read data and execute callback to dump data - flash_read_bytes(state->msg_buf, flash_addr + state->page_offset + sizeof(rec), rec.length); + pbl_flash_read(FLASH, flash_addr + state->page_offset + sizeof(rec), state->msg_buf, + rec.length); if (!state->line_cb(state->msg_buf, rec.length)) { if (++state->retry_count >= DUMP_LOG_MAX_RETRIES) { goto exit; diff --git a/src/fw/drivers/CMakeLists.txt b/src/fw/drivers/CMakeLists.txt index cca6c9862b..cddbb2306a 100644 --- a/src/fw/drivers/CMakeLists.txt +++ b/src/fw/drivers/CMakeLists.txt @@ -66,8 +66,6 @@ elseif(CONFIG_SOC_NRF52) ) pbl_library_named(driver_mcu) pbl_library_sources(stubs/mcu.c) - pbl_library_named(driver_qspi) - pbl_library_sources(nrf5/qspi.c) pbl_library_named(driver_nrf5_hfxo) pbl_library_sources(nrf5/hfxo.c) elseif(CONFIG_SOC_SF32LB52) @@ -78,8 +76,6 @@ elseif(CONFIG_SOC_SF32LB52) ) pbl_library_named(driver_mcu) pbl_library_sources(sf32lb52/mcu.c) - pbl_library_named(driver_qspi) - pbl_library_sources(sf32lb52/qspi.c) pbl_library_named(driver_rc10k) pbl_library_sources(sf32lb52/rc10k.c) endif() diff --git a/src/fw/drivers/flash/CMakeLists.txt b/src/fw/drivers/flash/CMakeLists.txt index f8261b1c19..7e595f71b9 100644 --- a/src/fw/drivers/flash/CMakeLists.txt +++ b/src/fw/drivers/flash/CMakeLists.txt @@ -2,16 +2,22 @@ # SPDX-License-Identifier: Apache-2.0 set(sources - cd_flash_driver.c - flash_api.c + flash.c flash_crc.c - flash_erase.c ) +if(CONFIG_FLASH_NRF5_QSPI) + list(APPEND sources nrf5_qspi.c) +endif() +if(CONFIG_FLASH_SF32LB52_MPI) + list(APPEND sources sf32lb52_mpi.c) +endif() if(CONFIG_FLASH_QEMU) - list(APPEND sources ../qemu/qemu_flash_hal.c) -elseif(CONFIG_FLASH_GD25LQ255E) + list(APPEND sources qemu.c) +endif() +if(CONFIG_FLASH_GD25LQ255E) list(APPEND sources gd25lq255e.c) -elseif(CONFIG_FLASH_GD25Q256E) +endif() +if(CONFIG_FLASH_GD25Q256E) list(APPEND sources gd25q256e.c) endif() pbl_library() diff --git a/src/fw/drivers/flash/Kconfig b/src/fw/drivers/flash/Kconfig index 13286485f8..67349dd01f 100644 --- a/src/fw/drivers/flash/Kconfig +++ b/src/fw/drivers/flash/Kconfig @@ -8,6 +8,27 @@ menuconfig FLASH if FLASH +config FLASH_NRF5_QSPI + bool "nRF5 QSPI NOR flash" + depends on SOC_NRF52 + default y + help + SPI NOR flash attached to the nRF5 QSPI peripheral. + +config FLASH_SF32LB52_MPI + bool "SF32LB52 MPI NOR flash" + depends on SOC_SF32LB52 + default y + help + SPI NOR flash attached to an SF32LB52 MPI controller. + +config FLASH_QEMU + bool "QEMU External Flash" + depends on SOC_QEMU + default y + help + Support for the QEMU external flash peripheral. + config FLASH_GD25LQ255E bool "GigaDevice GD25LQ255E" help @@ -18,12 +39,6 @@ config FLASH_GD25Q256E help Support for the GigaDevice GD25Q256E NOR flash. -config FLASH_QEMU - bool "QEMU External Flash" - default y if SOC_QEMU - help - Support for the QEMU external flash peripheral. - module = DRIVER_FLASH module-str = Flash source "subsys/logging/Kconfig.template.log_level" diff --git a/src/fw/drivers/flash/README.md b/src/fw/drivers/flash/README.md index ae575f165f..16b11269e8 100644 --- a/src/fw/drivers/flash/README.md +++ b/src/fw/drivers/flash/README.md @@ -1,16 +1,11 @@ Flash Memory Drivers -------------------- -Flash memory access is exposed through two separate APIs. The main API, defined -in `flash.h` and implemented in `flash_api.c`, is used almost everywhere. There -is also an alternate API used exclusively for performing core dumps, implemented -in `cd_flash_driver.c`. The alternate API is carefully implemented to be usable -regardless of how messed up the system state is in by not relying on any OS -services. +`include/pbl/drivers/flash.h` is the only flash API. It operates on a +`struct pbl_flash_device`, which boards define and export as `FLASH`. The +generic layer (`flash.c`) owns locking, write protection, blank checks and the +erase engine; drivers implement `struct pbl_flash_ops` for a controller and +never use OS services beyond what the generic layer hands them. -The flash APIs are written to be agnostic to the specific type of flash used. -They are written against the generic low-level driver interface defined in -`flash_impl.h`. Each low-level driver is specific to a combination of -flash memory part and microcontroller interface. The low-level drivers make no -use of OS services so that they can be used for both the main and core dump -driver APIs. +After `pbl_flash_coredump_init()` the same API works without locks, timers or +sleeping so that core dumps can be written from a fault handler. diff --git a/src/fw/drivers/flash/cd_flash_driver.c b/src/fw/drivers/flash/cd_flash_driver.c deleted file mode 100644 index 5e3cd47314..0000000000 --- a/src/fw/drivers/flash/cd_flash_driver.c +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include -#include -#include -#include "flash_region/flash_region.h" -#include "kernel/util/delay.h" - -#include "kernel/core_dump_private.h" - -static bool s_active; - -//! We have our own flash driver for coredump support because it must not use -//! any FreeRTOS constructs & we want to keep it as simple as possible. In -//! addition we want the flexibility to be able to reset the flash driver to -//! get it into a working state - -void cd_flash_init(void) { - // Reset & (re)initialize the flash HW - flash_impl_init(true /* coredump_mode */); - - // Protect the PRF region from writes - flash_impl_write_protect( - FLASH_REGION_SAFE_FIRMWARE_BEGIN, - (FLASH_REGION_SAFE_FIRMWARE_END - SECTOR_SIZE_BYTES)); - - s_active = true; -} - -bool cd_flash_active(void) { - return s_active; -} - -void cd_flash_erase_region(uint32_t start_addr, uint32_t total_bytes) { - CD_ASSERTN(((start_addr & SUBSECTOR_ADDR_MASK) == start_addr) && - ((total_bytes & SUBSECTOR_ADDR_MASK) == total_bytes)); - - while (total_bytes > 0) { - watchdog_feed(); - - uint32_t erase_size = 0; - if (((start_addr & SECTOR_ADDR_MASK) == start_addr) && - (total_bytes >= SECTOR_SIZE_BYTES)) { - erase_size = SECTOR_SIZE_BYTES; - flash_impl_erase_sector_begin(start_addr); - } else if ((start_addr & SUBSECTOR_ADDR_MASK) == start_addr && - (total_bytes >= SUBSECTOR_SIZE_BYTES)) { - erase_size = SUBSECTOR_SIZE_BYTES; - flash_impl_erase_subsector_begin(start_addr); - } else { - // Unaligned start address or unaligned erase size - CD_ASSERTN(0); - } - - status_t status; - while ((status = flash_impl_get_erase_status()) == E_BUSY) delay_us(100); - CD_ASSERTN(status == S_SUCCESS); - - total_bytes -= erase_size; - start_addr += erase_size; - } - - watchdog_feed(); -} - -uint32_t cd_flash_write_bytes(const void *buffer_ptr, uint32_t start_addr, - const uint32_t buffer_size) { - CD_ASSERTN(((start_addr + buffer_size) <= CORE_DUMP_FLASH_END) && - (int)start_addr >= CORE_DUMP_FLASH_START); - - const uint8_t *buffer = buffer_ptr; - uint32_t remaining = buffer_size; - - while (remaining) { - int written = flash_impl_write_page_begin(buffer, start_addr, remaining); - CD_ASSERTN(PASSED(written)); - status_t status; - while ((status = flash_impl_get_write_status()) == E_BUSY) { - delay_us(10); - } - CD_ASSERTN(PASSED(status)); - buffer += written; - start_addr += written; - remaining -= written; - } - return buffer_size; -} - -void cd_flash_read_bytes(void* buffer_ptr, uint32_t start_addr, - uint32_t buffer_size) { - flash_impl_read_sync(buffer_ptr, start_addr, buffer_size); -} - -status_t cd_flash_read_security_register(uint32_t addr, uint8_t *val) { - return flash_impl_read_security_register(addr, val); -} - -status_t cd_flash_security_register_is_locked(uint32_t addr, bool *locked) { - return flash_impl_security_register_is_locked(addr, locked); -} \ No newline at end of file diff --git a/src/fw/drivers/flash/flash.c b/src/fw/drivers/flash/flash.c new file mode 100644 index 0000000000..4eeb857cf0 --- /dev/null +++ b/src/fw/drivers/flash/flash.c @@ -0,0 +1,524 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include +#include +#include + +#include +#include +#include +#include "kernel/pebble_tasks.h" +#include "kernel/util/delay.h" +#include "kernel/util/sleep.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/util/math.h" +#include "system/passert.h" + +PBL_LOG_MODULE_DEFINE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); + +#define ERASE_MAX_RETRIES 3 +// Minimum time an erase gets to progress before a read or write suspends it. +#define ERASE_MIN_SLICE_MS 100 +#define ERASE_RESUME_AFTER_READ_MS 5 +#define ERASE_RESUME_AFTER_WRITE_MS 50 +// Stop feeding the watchdog once a single erase unit has taken this long. +#define ERASE_UNIT_WATCHDOG_MS 5000 +// Let lower priority tasks run in between synchronous erase units. +#define ERASE_YIELD_MS 4 + +static bool prv_in_range(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + return addr >= dev->base && len <= dev->size && addr - dev->base <= dev->size - len; +} + +static bool prv_is_protected(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + const struct pbl_flash_device_state *st = dev->state; + + return st->protect.enabled && addr < st->protect.end && addr + len > st->protect.start; +} + +// Lock held. +static bool prv_is_blank(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + uint32_t buf[32]; + + while (len > 0) { + size_t chunk = MIN(sizeof(buf), len); + int ret = dev->ops->read(dev, addr, buf, chunk); + PBL_ASSERT(ret == 0, "Flash read failed: %d", ret); + for (size_t i = 0; i < chunk; i++) { + if (((const uint8_t *)buf)[i] != 0xFF) { + return false; + } + } + addr += chunk; + len -= chunk; + } + + return true; +} + +// Lock held. +static void prv_erase_pause(const struct pbl_flash_device *dev) { + struct pbl_flash_device_state *st = dev->state; + + if (!st->erase.in_progress || st->erase.suspended || st->erase.done || + dev->ops->erase_suspend == NULL) { + return; + } + + psleep(ERASE_MIN_SLICE_MS); + int ret = dev->ops->erase_suspend(dev); + PBL_ASSERT(ret >= 0, "Erase suspend failure: %d", ret); + if (ret > 0) { + // Completed on its own; the next poll collects the result. + st->erase.done = true; + } else { + st->erase.suspended = true; + } +} + +// Lock held. +static void prv_erase_resume(const struct pbl_flash_device *dev) { + struct pbl_flash_device_state *st = dev->state; + + if (!st->erase.suspended) { + return; + } + + int ret = dev->ops->erase_resume(dev); + PBL_ASSERT(ret == 0, "Erase resume failure: %d", ret); + st->erase.suspended = false; +} + +static void prv_resume_timer_cb(void *ctx) { + const struct pbl_flash_device *dev = ctx; + + pbl_mutex_lock(&dev->state->lock, PBL_FOREVER); + prv_erase_resume(dev); + pbl_mutex_unlock(&dev->state->lock); +} + +static void prv_schedule_resume(const struct pbl_flash_device *dev, uint32_t delay_ms) { + if (dev->state->erase.suspended) { + new_timer_start(dev->state->resume_timer, delay_ms, prv_resume_timer_cb, (void *)dev, 0); + } +} + +static uint32_t prv_erase_unit(const struct pbl_flash_device *dev, uint32_t addr, uint32_t end) { + if ((addr & (dev->sector_size - 1)) == 0 && addr + dev->sector_size <= end) { + return dev->sector_size; + } + return dev->subsector_size; +} + +// Lock held. +static void prv_erase_unit_done(const struct pbl_flash_device *dev) { + struct pbl_flash_device_state *st = dev->state; + + PBL_ANALYTICS_ADD(flash_spi_erase_bytes, st->erase.unit); + st->erase.next += st->erase.unit; + st->erase.retries = 0; +} + +//! Advances the erase job by at most one hardware operation. +//! @return true when the job has finished (callback invoked), otherwise +//! @p wait_ms holds the delay before the next call (0: just yield). +static bool prv_erase_step(const struct pbl_flash_device *dev, uint32_t *wait_ms) { + struct pbl_flash_device_state *st = dev->state; + bool finished = false; + int status = 0; + + *wait_ms = 0; + pbl_mutex_lock(&st->lock, PBL_FOREVER); + + if (st->erase.in_progress) { + int ret = dev->ops->erase_status(dev); + if (ret == -EBUSY || ret == -EAGAIN) { + *wait_ms = MAX(1, st->erase.expected_ms / 8); + goto out; + } + st->erase.in_progress = false; + st->erase.done = false; + if (ret == 0) { + prv_erase_unit_done(dev); + } else if (st->erase.retries < ERASE_MAX_RETRIES) { + st->erase.retries++; + PBL_LOG_DBG("Erase of 0x%" PRIx32 " failed (%d), retrying", st->erase.next, ret); + } else { + status = ret; + finished = true; + goto out; + } + } + + if (st->erase.next >= st->erase.end) { + finished = true; + goto out; + } + + uint32_t addr = st->erase.next; + uint32_t unit = prv_erase_unit(dev, addr, st->erase.end); + st->erase.unit = unit; + + if (prv_is_protected(dev, addr, unit)) { + status = -EACCES; + finished = true; + goto out; + } + + if (prv_is_blank(dev, addr, unit)) { + st->erase.next += unit; + goto out; + } + + int ret = dev->ops->erase_begin(dev, addr, unit); + if (ret != 0) { + status = ret; + finished = true; + goto out; + } + + if (dev->ops->erase_status != NULL) { + st->erase.in_progress = true; + st->erase.expected_ms = + (unit == dev->sector_size) ? dev->sector_erase_ms : dev->subsector_erase_ms; + *wait_ms = MAX(1, st->erase.expected_ms * 7 / 8); + } else { + prv_erase_unit_done(dev); + } + +out: + if (finished) { + pbl_flash_erase_cb_t cb = st->erase.cb; + void *ctx = st->erase.ctx; + + pbl_mutex_unlock(&st->lock); + pbl_sem_give(&st->erase_sem); + // No locks held so that the callback can start another erase. + cb(ctx, status); + } else { + pbl_mutex_unlock(&st->lock); + } + + return finished; +} + +static void prv_poll_timer_cb(void *ctx) { + const struct pbl_flash_device *dev = ctx; + uint32_t wait_ms; + + if (!prv_erase_step(dev, &wait_ms)) { + new_timer_start(dev->state->poll_timer, wait_ms, prv_poll_timer_cb, ctx, 0); + } +} + +static size_t prv_erase_len(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + PBL_ASSERTN((addr & (dev->subsector_size - 1)) == 0); + len = (len + dev->subsector_size - 1) & ~(size_t)(dev->subsector_size - 1); + PBL_ASSERTN(prv_in_range(dev, addr, len)); + return len; +} + +static void prv_erase_start(const struct pbl_flash_device *dev, uint32_t addr, size_t len, + pbl_flash_erase_cb_t cb, void *ctx) { + struct pbl_flash_device_state *st = dev->state; + + pbl_sem_take(&st->erase_sem, PBL_FOREVER); + pbl_mutex_lock(&st->lock, PBL_FOREVER); + st->erase.in_progress = false; + st->erase.suspended = false; + st->erase.done = false; + st->erase.next = addr; + st->erase.end = addr + len; + st->erase.retries = 0; + st->erase.cb = cb; + st->erase.ctx = ctx; + pbl_mutex_unlock(&st->lock); +} + +static void prv_blocking_erase_cb(void *ctx, int status) { + *(int *)ctx = status; +} + +static int prv_erase_coredump(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + while (len > 0) { + uint32_t unit = prv_erase_unit(dev, addr, addr + len); + + if (prv_is_protected(dev, addr, unit)) { + return -EACCES; + } + watchdog_feed(); + int ret = dev->ops->erase_begin(dev, addr, unit); + if (ret != 0) { + return ret; + } + if (dev->ops->erase_status != NULL) { + while ((ret = dev->ops->erase_status(dev)) == -EBUSY) { + delay_us(100); + } + if (ret != 0) { + return ret; + } + } + addr += unit; + len -= unit; + } + watchdog_feed(); + + return 0; +} + +int pbl_flash_init(const struct pbl_flash_device *dev) { + struct pbl_flash_device_state *st = dev->state; + + pbl_mutex_init(&st->lock); + pbl_sem_init(&st->erase_sem, 1, 1); + st->poll_timer = new_timer_create(); + st->resume_timer = new_timer_create(); + + int ret = dev->ops->init(dev); + PBL_ASSERT(ret == 0, "Flash init failed: %d", ret); + st->initialized = true; + + return ret; +} + +int pbl_flash_coredump_init(const struct pbl_flash_device *dev) { + dev->state->coredump = true; + return dev->ops->init(dev); +} + +void pbl_flash_stop(const struct pbl_flash_device *dev) { + struct pbl_flash_device_state *st = dev->state; + + if (!st->initialized) { + return; + } + + pbl_mutex_lock(&st->lock, PBL_FOREVER); + if (st->erase.in_progress) { + new_timer_stop(st->resume_timer); + prv_erase_resume(dev); + } + pbl_mutex_unlock(&st->lock); + + while (__atomic_load_n(&st->erase.in_progress, __ATOMIC_SEQ_CST)) { + psleep(10); + } +} + +int pbl_flash_read(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len) { + struct pbl_flash_device_state *st = dev->state; + + PBL_ASSERTN(prv_in_range(dev, addr, len)); + if (len == 0) { + return 0; + } + + if (st->coredump) { + return dev->ops->read(dev, addr, buf, len); + } + + pbl_mutex_lock(&st->lock, PBL_FOREVER); + prv_erase_pause(dev); + prv_schedule_resume(dev, ERASE_RESUME_AFTER_READ_MS); + int ret = dev->ops->read(dev, addr, buf, len); + PBL_ASSERT(ret == 0, "Flash read failed: %d", ret); + pbl_mutex_unlock(&st->lock); + + return 0; +} + +int pbl_flash_write(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, + size_t len) { + struct pbl_flash_device_state *st = dev->state; + + PBL_ASSERTN(prv_in_range(dev, addr, len)); + if (len == 0) { + return 0; + } + + if (st->coredump) { + if (prv_is_protected(dev, addr, len)) { + return -EACCES; + } + return dev->ops->write(dev, addr, buf, len); + } + + pbl_mutex_lock(&st->lock, PBL_FOREVER); + PBL_ASSERT(!prv_is_protected(dev, addr, len), "Write to protected flash at 0x%" PRIx32, addr); + prv_erase_pause(dev); + prv_schedule_resume(dev, ERASE_RESUME_AFTER_WRITE_MS); + PBL_ANALYTICS_ADD(flash_spi_write_bytes, len); + int ret = dev->ops->write(dev, addr, buf, len); + PBL_ASSERT(ret == 0, "Flash write failed: %d", ret); + pbl_mutex_unlock(&st->lock); + + return 0; +} + +int pbl_flash_erase(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + struct pbl_flash_device_state *st = dev->state; + int status = 0; + uint32_t wait_ms; + uint32_t unit_waited_ms = 0; + uint32_t last_next = addr; + unsigned int yields = 0; + + len = prv_erase_len(dev, addr, len); + if (len == 0) { + return 0; + } + + if (st->coredump) { + return prv_erase_coredump(dev, addr, len); + } + + prv_erase_start(dev, addr, len, prv_blocking_erase_cb, &status); + + while (!prv_erase_step(dev, &wait_ms)) { + if (wait_ms > 0) { + psleep(wait_ms); + } else if ((++yields % 2) == 0) { + psleep(ERASE_YIELD_MS); + } + + // The resume timer callback may be starved by this task. See PBL-25741. + uint32_t resume_remaining_ms; + if (new_timer_scheduled(st->resume_timer, &resume_remaining_ms) && resume_remaining_ms == 0) { + prv_resume_timer_cb((void *)dev); + } + + uint32_t next = __atomic_load_n(&st->erase.next, __ATOMIC_RELAXED); + if (next != last_next) { + last_next = next; + unit_waited_ms = 0; + } else { + unit_waited_ms += wait_ms; + } + + // Erases can take seconds on worn parts; stop appeasing the watchdog only + // if a single unit never completes. + if (unit_waited_ms < ERASE_UNIT_WATCHDOG_MS) { +#ifdef CONFIG_IS_BIGBOARD + task_watchdog_bit_set_all(); +#else + task_watchdog_bit_set(pebble_task_get_current()); +#endif + } + } + + PBL_ASSERT(status == 0, "Flash erase failure: %d", status); + + return 0; +} + +int pbl_flash_erase_async(const struct pbl_flash_device *dev, uint32_t addr, size_t len, + pbl_flash_erase_cb_t cb, void *ctx) { + PBL_ASSERTN(!dev->state->coredump); + + len = prv_erase_len(dev, addr, len); + if (len == 0) { + cb(ctx, 0); + return 0; + } + + prv_erase_start(dev, addr, len, cb, ctx); + prv_poll_timer_cb((void *)dev); + + return 0; +} + +bool pbl_flash_is_erased(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + struct pbl_flash_device_state *st = dev->state; + + PBL_ASSERTN(prv_in_range(dev, addr, len)); + + if (st->coredump) { + return prv_is_blank(dev, addr, len); + } + + pbl_mutex_lock(&st->lock, PBL_FOREVER); + prv_erase_pause(dev); + prv_schedule_resume(dev, ERASE_RESUME_AFTER_READ_MS); + bool blank = prv_is_blank(dev, addr, len); + pbl_mutex_unlock(&st->lock); + + return blank; +} + +int pbl_flash_protect(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + struct pbl_flash_device_state *st = dev->state; + + PBL_ASSERTN(prv_in_range(dev, addr, len)); + + pbl_mutex_lock(&st->lock, PBL_FOREVER); + st->protect.start = addr; + st->protect.end = addr + len; + st->protect.enabled = true; + pbl_mutex_unlock(&st->lock); + + return 0; +} + +int pbl_flash_unprotect(const struct pbl_flash_device *dev) { + struct pbl_flash_device_state *st = dev->state; + + pbl_mutex_lock(&st->lock, PBL_FOREVER); + st->protect.enabled = false; + pbl_mutex_unlock(&st->lock); + + return 0; +} + +void pbl_flash_power_down(const struct pbl_flash_device *dev) { + if (dev->ops->power_down != NULL) { + dev->ops->power_down(dev); + } +} + +void pbl_flash_power_up(const struct pbl_flash_device *dev) { + if (dev->ops->power_up != NULL) { + dev->ops->power_up(dev); + } +} + +#define SEC_REG_OP(dev, op, ...) \ + do { \ + struct pbl_flash_device_state *st = (dev)->state; \ + int ret; \ + if ((dev)->ops->op == NULL) { \ + return -ENOTSUP; \ + } \ + if (st->coredump) { \ + return (dev)->ops->op(dev, __VA_ARGS__); \ + } \ + pbl_mutex_lock(&st->lock, PBL_FOREVER); \ + ret = (dev)->ops->op(dev, __VA_ARGS__); \ + pbl_mutex_unlock(&st->lock); \ + return ret; \ + } while (0) + +int pbl_flash_sec_reg_read(const struct pbl_flash_device *dev, uint32_t addr, uint8_t *val) { + SEC_REG_OP(dev, sec_reg_read, addr, val); +} + +int pbl_flash_sec_reg_write(const struct pbl_flash_device *dev, uint32_t addr, uint8_t val) { + SEC_REG_OP(dev, sec_reg_write, addr, val); +} + +int pbl_flash_sec_reg_erase(const struct pbl_flash_device *dev, uint32_t addr) { + SEC_REG_OP(dev, sec_reg_erase, addr); +} + +int pbl_flash_sec_reg_is_locked(const struct pbl_flash_device *dev, uint32_t addr, bool *locked) { + SEC_REG_OP(dev, sec_reg_is_locked, addr, locked); +} + +#ifdef CONFIG_RECOVERY_FW +int pbl_flash_sec_reg_lock(const struct pbl_flash_device *dev, uint32_t addr) { + SEC_REG_OP(dev, sec_reg_lock, addr); +} +#endif diff --git a/src/fw/drivers/flash/flash_api.c b/src/fw/drivers/flash/flash_api.c deleted file mode 100644 index 822fa0958b..0000000000 --- a/src/fw/drivers/flash/flash_api.c +++ /dev/null @@ -1,491 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include -#include - -#include -#include -#include - -#include -#include -#include "flash_region/flash_region.h" -#include "pbl/kernel/mutex.h" -#include "pbl/kernel/types.h" -#include "process_management/worker_manager.h" -#include "pbl/services/new_timer/new_timer.h" -#include "pbl/services/analytics/analytics.h" -#include -#include "system/passert.h" -#include "kernel/util/sleep.h" - -#include "pbl/kernel/sem.h" - -PBL_LOG_MODULE_DEFINE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); - -#define MAX_ERASE_RETRIES (3) - -static PBL_MUTEX_DEFINE(s_flash_lock); -static bool s_flash_initialized; -static PBL_SEM_DEFINE(s_erase_semphr, 0, 1); - -static struct FlashEraseContext { - bool in_progress; - bool suspended; - bool is_subsector; - uint8_t retries; - PebbleTask task; - uint32_t address; - FlashOperationCompleteCb on_complete_cb; - void *cb_context; - uint32_t expected_duration; -} s_erase = { 0 }; - -static TimerID s_erase_poll_timer; -static TimerID s_erase_suspend_timer; - -void flash_init(void) { - flash_impl_init(false /* coredump_mode */); - s_flash_initialized = true; - - pbl_sem_give(&s_erase_semphr); - s_erase_poll_timer = new_timer_create(); - s_erase_suspend_timer = new_timer_create(); - - flash_erase_init(); -} - -#if UNITTEST -void flash_api_reset_for_test(void) { - s_erase = (struct FlashEraseContext) {0}; - s_flash_initialized = false; -} - -TimerID flash_api_get_erase_poll_timer_for_test(void) { - return s_erase_poll_timer; -} -#endif - -//! Assumes that s_flash_lock is held. -static void prv_erase_pause(void) { - if (s_erase.in_progress && !s_erase.suspended) { - // If an erase is in progress, make sure it gets at least a minimum time slice to progress. - // If not, the successive kicking of the suspend timer could starve it out completely - psleep(100); - task_watchdog_bit_set(s_erase.task); - status_t status = flash_impl_erase_suspend(s_erase.address); - PBL_ASSERT(PASSED(status), "Erase suspend failure: %" PRId32, status); - if (status == S_NO_ACTION_REQUIRED) { - // The erase has already completed. No need to resume. - s_erase.in_progress = false; - } else { - s_erase.suspended = true; - } - } -} - -//! Assumes that s_flash_lock is held. -static void prv_erase_resume(void) { - if (s_erase.suspended) { - status_t status = flash_impl_erase_resume(s_erase.address); - PBL_ASSERT(PASSED(status), "Erase resume failure: %" PRId32, status); - s_erase.suspended = false; - } -} - -static void prv_erase_suspend_timer_cb(void *unused) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - prv_erase_resume(); - pbl_mutex_unlock(&s_flash_lock); -} - -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, - uint32_t buffer_size) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - // TODO: use DMA when possible - // TODO: be smarter about pausing erases. Some flash chips allow concurrent - // reads while an erase is in progress, as long as the read is to another bank - // than the one being erased. - prv_erase_pause(); - if (s_erase.suspended) { - new_timer_start(s_erase_suspend_timer, 5, prv_erase_suspend_timer_cb, NULL, 0); - } - flash_impl_read_sync(buffer, start_addr, buffer_size); - pbl_mutex_unlock(&s_flash_lock); -} - -#ifdef TEST_FLASH_LOCK_PROTECTION -static bool s_assert_write_error = false; -void flash_expect_program_failure(bool expect_failure) { - s_assert_write_error = expect_failure; -} -#endif - -void flash_write_bytes(const uint8_t *buffer, uint32_t start_addr, - uint32_t buffer_size) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - prv_erase_pause(); - if (s_erase.suspended) { - new_timer_start(s_erase_suspend_timer, 50, prv_erase_suspend_timer_cb, NULL, 0); - } - - PBL_ANALYTICS_ADD(flash_spi_write_bytes, buffer_size); - - while (buffer_size) { - int written = flash_impl_write_page_begin(buffer, start_addr, buffer_size); - PBL_ASSERT( -#ifdef TEST_FLASH_LOCK_PROTECTION - s_assert_write_error || -#endif - PASSED(written), - "flash_impl_write_page_begin failed: %d", written); - status_t status; - while ((status = flash_impl_get_write_status()) == E_BUSY) { - psleep(0); - } -#ifdef TEST_FLASH_LOCK_PROTECTION - if (s_assert_write_error) { - PBL_ASSERT(FAILED(status), "flash write unexpectedly succeeded: %" PRId32, - status); - } else { -#endif - PBL_ASSERT(PASSED(status), "flash_impl_get_write_status returned %" PRId32, - status); -#ifdef TEST_FLASH_LOCK_PROTECTION - } -#endif - buffer += written; - start_addr += written; - buffer_size -= written; - // Give higher-priority tasks a chance to access the flash in between - // each page write. - // TODO: uncomment the lines below to resolve PBL-17503 - // if (buffer_size) { - // pbl_mutex_unlock(&s_flash_lock); - // pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - // } - } - pbl_mutex_unlock(&s_flash_lock); -} - -// Returns 0 if the erase has completed, or a non-zero expected duration (in -// ms) if not. If the erase has not finished (non-zero has been returned), the -// caller is responsible for calling the prv_flash_erase_poll() method until -// the erase completes. -static uint32_t prv_flash_erase_start(uint32_t addr, - FlashOperationCompleteCb on_complete_cb, - void *context, - bool is_subsector, - uint8_t retries) { - pbl_sem_take(&s_erase_semphr, PBL_FOREVER); - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - PBL_ASSERTN(s_erase.in_progress == false); - s_erase = (struct FlashEraseContext) { - .in_progress = true, - .task = pebble_task_get_current(), - .retries = retries, - // FIXME: We should just assert that the address is already aligned. If - // someone is depending on this behaviour without already knowing the range - // that's being erased they're going to have a bad time. This will probably - // cause some client fallout though, so tackle this later. - .is_subsector = is_subsector, - .address = is_subsector? flash_impl_get_subsector_base_address(addr) - : flash_impl_get_sector_base_address(addr), - .on_complete_cb = on_complete_cb, - .cb_context = context, - .expected_duration = is_subsector? - flash_impl_get_typical_subsector_erase_duration_ms() : - flash_impl_get_typical_sector_erase_duration_ms(), - }; - status_t status = is_subsector? flash_impl_blank_check_subsector(addr) - : flash_impl_blank_check_sector(addr); - PBL_ASSERT(PASSED(status), "Blank check error: %" PRId32, status); - if (status != S_FALSE) { - s_erase.in_progress = false; - pbl_mutex_unlock(&s_flash_lock); - pbl_sem_give(&s_erase_semphr); - // Only run the callback with no locks held so that the callback won't - // deadlock if it kicks off another sector erase. - on_complete_cb(context, S_NO_ACTION_REQUIRED); - return 0; - } - - status = is_subsector? flash_impl_erase_subsector_begin(addr) - : flash_impl_erase_sector_begin(addr); - - if (PASSED(status)) { - pbl_mutex_unlock(&s_flash_lock); - return (s_erase.expected_duration * 7 / 8); - } else { - s_erase.in_progress = false; - pbl_mutex_unlock(&s_flash_lock); - pbl_sem_give(&s_erase_semphr); - // Only run the callback with no locks held so that the callback won't - // deadlock if it kicks off another sector erase. - on_complete_cb(context, status); - return 0; - } -} - -// Returns non-zero expected remaining time if the erase has not finished. If the erase -// has finished it will re-enable stop-mode, clear the in_progress flag and call the -// completed callback before returning 0. -static uint32_t prv_flash_erase_poll(void) { - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - status_t status = flash_impl_get_erase_status(); - bool erase_finished; - struct FlashEraseContext saved_ctx = s_erase; - switch (status) { - case E_BUSY: - case E_AGAIN: - erase_finished = false; - break; - case S_SUCCESS: - default: - // Success or failure; the erase has finished either way. - erase_finished = true; - break; - } - - if (erase_finished) { - s_erase.in_progress = false; - } - pbl_mutex_unlock(&s_flash_lock); - - if (!erase_finished) { - return s_erase.expected_duration / 8; - } - - pbl_sem_give(&s_erase_semphr); - if (status == E_ERROR && saved_ctx.retries < MAX_ERASE_RETRIES) { - // Try issuing the erase again. It might succeed this time around. - PBL_LOG_DBG("Erase of 0x%"PRIx32" failed (attempt %d)." - " Trying again...", saved_ctx.address, saved_ctx.retries); - return prv_flash_erase_start( - saved_ctx.address, saved_ctx.on_complete_cb, saved_ctx.cb_context, - saved_ctx.is_subsector, saved_ctx.retries + 1); - } else { - if (status == S_SUCCESS) { - PBL_ANALYTICS_ADD(flash_spi_erase_bytes, - saved_ctx.is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES); - } - // Only run the callback with no locks held so that the callback won't - // deadlock if it kicks off another sector erase. - saved_ctx.on_complete_cb(saved_ctx.cb_context, status); - return 0; - } -} - -// Timer callback that checks to see if the erase has finished. Used by the non-blocking -// routines. -static void prv_flash_erase_timer_cb(void *context) { - uint32_t remaining_ms = prv_flash_erase_poll(); - if (remaining_ms) { - // Erase is in progress or suspended; poll again later. - new_timer_start(s_erase_poll_timer, remaining_ms, prv_flash_erase_timer_cb, NULL, 0); - } -} - -static void prv_flash_erase_async( - uint32_t sector_addr, bool is_subsector, FlashOperationCompleteCb on_complete_cb, - void *context) { - uint32_t remaining_ms = prv_flash_erase_start(sector_addr, on_complete_cb, - context, is_subsector, 0); - if (remaining_ms) { - // Start timer that will periodically check for the erase to complete - new_timer_start(s_erase_poll_timer, remaining_ms, prv_flash_erase_timer_cb, NULL, 0); - } -} - -static void prv_blocking_erase_complete(void *context, status_t status) { - PBL_ASSERT(PASSED(status), "Flash erase failure: %" PRId32, status); -} - -static void prv_flash_erase_blocking(uint32_t sector_addr, bool is_subsector) { - uint32_t total_time_spent_waiting_ms = 0; - - uint32_t remaining_ms = prv_flash_erase_start( - sector_addr, prv_blocking_erase_complete, NULL, is_subsector, 0); - while (remaining_ms) { - psleep(remaining_ms); - total_time_spent_waiting_ms += remaining_ms; - - remaining_ms = prv_flash_erase_poll(); - - // check to see if the cb responsible for resuming erases should have run - // but is blocked. See PBL-25741 for details - uint32_t erase_suspend_time_remaining; - if (new_timer_scheduled(s_erase_suspend_timer, &erase_suspend_time_remaining) && - (erase_suspend_time_remaining == 0)) { - prv_erase_suspend_timer_cb(NULL); - } - - // An erase can take a long time, especially if the erase needs to be - // retried. Appease the watchdog so that it doesn't get angry when an - // erase takes >6 seconds. - // - // After a certain amount of time passes, stop kicking the watchdog. This is to handle a case - // where the erase never finishes or takes an unheard of amount of time to complete. Just let - // the watchdog kill us in this case. - static const uint32_t FLASH_ERASE_BLOCKING_TIMEOUT_MS = 5000; - if (total_time_spent_waiting_ms < FLASH_ERASE_BLOCKING_TIMEOUT_MS) { -#ifdef CONFIG_IS_BIGBOARD - // Our bigboards have had a hard life and they have some fairly abused flash chips, and we - // run into 5+ second erases pretty regularly. We're not holding the flash lock while we're - // doing this, so other threads are allowed to use flash, but it's pretty common to hold - // other locks while waiting for a flash operation to complete, leading to other tasks - // triggering their task watchdogs before this erase completes. Let's kick all watchdogs - // instead. The downside to this is that it may take us longer to detect another thread is - // stuck, but we should still detect it eventually as long as we're not constantly erasing. - task_watchdog_bit_set_all(); -#else - // Just kick the watchdog for the current task. This should give us more accurate watchdog - // behaviour and sealed watches haven't been abused as much and shouldn't have extremely - // long erase problems. - task_watchdog_bit_set(pebble_task_get_current()); -#endif - } - } -} - -void flash_erase_sector(uint32_t sector_addr, - FlashOperationCompleteCb on_complete_cb, - void *context) { - prv_flash_erase_async(sector_addr, false /* is_subsector */, on_complete_cb, context); -} - -void flash_erase_subsector(uint32_t sector_addr, - FlashOperationCompleteCb on_complete_cb, - void *context) { - prv_flash_erase_async(sector_addr, true /* is_subsector */, on_complete_cb, context); -} - -void flash_erase_sector_blocking(uint32_t sector_addr) { - prv_flash_erase_blocking(sector_addr, false /* is_subsector */); -} - -void flash_erase_subsector_blocking(uint32_t subsector_addr) { - prv_flash_erase_blocking(subsector_addr, true /* is_subsector */); -} - -void flash_prf_set_protection(bool do_protect) { - status_t status; - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - if (do_protect) { - status = flash_impl_write_protect( - FLASH_REGION_SAFE_FIRMWARE_BEGIN, - (FLASH_REGION_SAFE_FIRMWARE_END - SECTOR_SIZE_BYTES)); - } else { - status = flash_impl_unprotect(); - } - PBL_ASSERT(PASSED(status), "flash_prf_set_protection failed: %" PRId32, status); - pbl_mutex_unlock(&s_flash_lock); -} - -static bool prv_is_initialized(void) { - return s_flash_initialized; -} - -void flash_stop(void) { - if (!prv_is_initialized()) { - // Not yet initialized, nothing to do. - return; - } - - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - if (s_erase.in_progress) { - new_timer_stop(s_erase_suspend_timer); - prv_erase_resume(); - pbl_mutex_unlock(&s_flash_lock); - while (__atomic_load_n(&s_erase.in_progress, __ATOMIC_SEQ_CST)) { - psleep(10); - } - } -} - -uint32_t flash_get_sector_base_address(uint32_t flash_addr) { - return flash_impl_get_sector_base_address(flash_addr); -} - -uint32_t flash_get_subsector_base_address(uint32_t flash_addr) { - return flash_impl_get_subsector_base_address(flash_addr); -} - -void flash_power_down_for_stop_mode(void) { - flash_impl_enter_low_power_mode(); -} - -void flash_power_up_after_stop_mode(void) { - flash_impl_exit_low_power_mode(); -} - -bool flash_sector_is_erased(uint32_t sector_addr) { - return flash_impl_blank_check_sector(flash_impl_get_sector_base_address(sector_addr)); -} - -bool flash_subsector_is_erased(uint32_t sector_addr) { - return flash_impl_blank_check_subsector(flash_impl_get_subsector_base_address(sector_addr)); -} - -status_t flash_read_security_register(uint32_t addr, uint8_t *val) { - status_t status; - - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - status = flash_impl_read_security_register(addr, val); - pbl_mutex_unlock(&s_flash_lock); - - return status; -} - -status_t flash_security_register_is_locked(uint32_t address, bool *locked) { - status_t status; - - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - status = flash_impl_security_register_is_locked(address, locked); - pbl_mutex_unlock(&s_flash_lock); - - return status; -} - -status_t flash_erase_security_register(uint32_t addr) { - status_t status; - - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - status = flash_impl_erase_security_register(addr); - pbl_mutex_unlock(&s_flash_lock); - - return status; -} - -status_t flash_write_security_register(uint32_t addr, uint8_t val) { - status_t status; - - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - status = flash_impl_write_security_register(addr, val); - pbl_mutex_unlock(&s_flash_lock); - - return status; -} - -const FlashSecurityRegisters *flash_security_registers_info(void) { - return flash_impl_security_registers_info(); -} - -#ifdef CONFIG_RECOVERY_FW -status_t flash_lock_security_register(uint32_t addr) { - status_t status; - - pbl_mutex_lock(&s_flash_lock, PBL_FOREVER); - status = flash_impl_lock_security_register(addr); - pbl_mutex_unlock(&s_flash_lock); - - return status; -} -#endif // CONFIG_RECOVERY_FW - -#include "console/prompt.h" -void command_flash_unprotect(void) { - flash_impl_unprotect(); - prompt_send_response("OK"); -} diff --git a/src/fw/drivers/flash/flash_crc.c b/src/fw/drivers/flash/flash_crc.c index 8aadb65c94..035007d673 100644 --- a/src/fw/drivers/flash/flash_crc.c +++ b/src/fw/drivers/flash/flash_crc.c @@ -1,71 +1,62 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ /* SPDX-License-Identifier: Apache-2.0 */ #include -#include "kernel/pbl_malloc.h" +#include + #include +#include "kernel/pbl_malloc.h" #include "pbl/util/crc32.h" #include "util/legacy_checksum.h" -#include - PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); -static size_t prv_allocate_crc_buffer(void **buffer) { - // Try to allocate a big buffer for reading flash data. If we can't, - // use a smaller one. - unsigned int chunk_size = 1024; +static size_t prv_allocate_buffer(void **buffer) { + size_t chunk_size = 1024; + *buffer = kernel_malloc(chunk_size); - if (!*buffer) { + if (*buffer == NULL) { PBL_LOG_WRN("Insufficient memory for a large CRC buffer, going slow"); - chunk_size = 128; *buffer = kernel_malloc_check(chunk_size); } + return chunk_size; } -uint32_t flash_crc32(uint32_t flash_addr, uint32_t num_bytes) { +uint32_t pbl_flash_crc32(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { void *buffer; - unsigned int chunk_size = prv_allocate_crc_buffer(&buffer); - + size_t chunk_size = prv_allocate_buffer(&buffer); uint32_t crc = CRC32_INIT; - while (num_bytes > chunk_size) { - flash_read_bytes(buffer, flash_addr, chunk_size); - crc = crc32(crc, buffer, chunk_size); - num_bytes -= chunk_size; - flash_addr += chunk_size; + while (len > 0) { + size_t chunk = (len < chunk_size) ? len : chunk_size; + pbl_flash_read(dev, addr, buffer, chunk); + crc = crc32(crc, buffer, chunk); + addr += chunk; + len -= chunk; } - flash_read_bytes(buffer, flash_addr, num_bytes); - crc = crc32(crc, buffer, num_bytes); - kernel_free(buffer); return crc; } -uint32_t flash_calculate_legacy_defective_checksum(uint32_t flash_addr, - uint32_t num_bytes) { +uint32_t pbl_flash_legacy_checksum(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { void *buffer; - unsigned int chunk_size = prv_allocate_crc_buffer(&buffer); - + size_t chunk_size = prv_allocate_buffer(&buffer); LegacyChecksum checksum; - legacy_defective_checksum_init(&checksum); - - while (num_bytes > chunk_size) { - flash_read_bytes(buffer, flash_addr, chunk_size); - legacy_defective_checksum_update(&checksum, buffer, chunk_size); - num_bytes -= chunk_size; - flash_addr += chunk_size; + legacy_defective_checksum_init(&checksum); + while (len > 0) { + size_t chunk = (len < chunk_size) ? len : chunk_size; + pbl_flash_read(dev, addr, buffer, chunk); + legacy_defective_checksum_update(&checksum, buffer, chunk); + addr += chunk; + len -= chunk; } - flash_read_bytes(buffer, flash_addr, num_bytes); - legacy_defective_checksum_update(&checksum, buffer, num_bytes); - kernel_free(buffer); return legacy_defective_checksum_finish(&checksum); diff --git a/src/fw/drivers/flash/flash_erase.c b/src/fw/drivers/flash/flash_erase.c deleted file mode 100644 index 08e4f4d229..0000000000 --- a/src/fw/drivers/flash/flash_erase.c +++ /dev/null @@ -1,128 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include -#include - -#include "flash_region/flash_region.h" -#include "pbl/services/new_timer/new_timer.h" -#include -#include "system/passert.h" -#include "pbl/util/attributes.h" -#include "pbl/util/math.h" - -#include "pbl/kernel/sem.h" - -PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); - -static PBL_SEM_DEFINE(s_erase_mutex, 0, 1); -static struct FlashRegionEraseState { - uint32_t next_erase_addr; - uint32_t end_addr; - FlashOperationCompleteCb on_complete; - void *on_complete_context; -} s_erase_state; - -static void prv_erase_next_async(void *ignored); - -T_STATIC void prv_lock_erase_mutex(void); -T_STATIC void prv_unlock_erase_mutex(void); -#if !UNITTEST -void flash_erase_init(void) { - pbl_sem_give(&s_erase_mutex); -} - -static void prv_lock_erase_mutex(void) { - pbl_sem_take(&s_erase_mutex, PBL_FOREVER); -} - -static void prv_unlock_erase_mutex(void) { - pbl_sem_give(&s_erase_mutex); -} -#endif - -static void prv_async_erase_done_cb(void *ignored, status_t result) { - if (PASSED(result) && - s_erase_state.next_erase_addr < s_erase_state.end_addr) { - // Chain the next erase from a new callback to prevent recursion (and the - // potential for a stack overflow) if the flash_erase_sector calls the - // completion callback asynchronously. - if (!new_timer_add_work_callback(prv_erase_next_async, NULL)) { - PBL_LOG_ERR("Failed to enqueue callback; aborting erase"); - prv_unlock_erase_mutex(); - s_erase_state.on_complete(s_erase_state.on_complete_context, E_INTERNAL); - } - } else { - prv_unlock_erase_mutex(); - s_erase_state.on_complete(s_erase_state.on_complete_context, result); - } -} - -static void prv_erase_next_async(void *ignored) { - uint32_t addr = s_erase_state.next_erase_addr; - if ((addr & ~SECTOR_ADDR_MASK) == 0 && - addr + SECTOR_SIZE_BYTES <= s_erase_state.end_addr) { - s_erase_state.next_erase_addr += SECTOR_SIZE_BYTES; - flash_erase_sector(addr, prv_async_erase_done_cb, NULL); - } else { - // Fall back to a subsector erase - s_erase_state.next_erase_addr += SUBSECTOR_SIZE_BYTES; - PBL_ASSERTN(s_erase_state.next_erase_addr <= s_erase_state.end_addr); - flash_erase_subsector(addr, prv_async_erase_done_cb, NULL); - } -} - -void flash_erase_optimal_range( - uint32_t min_start, uint32_t max_start, uint32_t min_end, uint32_t max_end, - FlashOperationCompleteCb on_complete, void *context) { - PBL_ASSERTN(((min_start & (~SUBSECTOR_ADDR_MASK)) == 0) && - ((max_end & (~SUBSECTOR_ADDR_MASK)) == 0) && - (min_start <= max_start) && - (max_start <= min_end) && - (min_end <= max_end)); - - // We want to erase the sector that starts immediately below max_start but - // after min_start. If no sector boundary exists between the two, we need to - // start erasing sectors after min_start and backfill with subsector erases. - int32_t sector_start = (max_start & SECTOR_ADDR_MASK); - int32_t subsector_start = (max_start & SUBSECTOR_ADDR_MASK); - if (sector_start < (int32_t) min_start) { - sector_start += SECTOR_SIZE_BYTES; - } - - // We want to erase ending after min_end but before max_end. If that ends - // running past the end of max_end, we need to erase starting with the sector - // before and fill in with subsector erases. - int32_t sector_end = ((min_end - 1) & SECTOR_ADDR_MASK) + SECTOR_SIZE_BYTES; - int32_t subsector_end = - ((min_end - 1) & SUBSECTOR_ADDR_MASK) + SUBSECTOR_SIZE_BYTES; - if (sector_end > (int32_t) max_end) { - sector_end -= SECTOR_SIZE_BYTES; - } - - int32_t start_addr = MIN(sector_start, subsector_start); - int32_t end_addr = MAX(sector_end, subsector_end); - - if (sector_start >= sector_end) { - // Can't erase any full sectors; just erase subsectors the whole way. - start_addr = subsector_start; - end_addr = subsector_end; - } - - if (start_addr == end_addr) { - // Nothing to do! - on_complete(context, S_NO_ACTION_REQUIRED); - return; - } - - prv_lock_erase_mutex(); - - s_erase_state = (struct FlashRegionEraseState) { - .next_erase_addr = start_addr, - .end_addr = end_addr, - .on_complete = on_complete, - .on_complete_context = context, - }; - - prv_erase_next_async(NULL); -} diff --git a/src/fw/drivers/flash/gd25lq255e.c b/src/fw/drivers/flash/gd25lq255e.c index d7f75b0b03..647cd88d5e 100644 --- a/src/fw/drivers/flash/gd25lq255e.c +++ b/src/fw/drivers/flash/gd25lq255e.c @@ -1,38 +1,22 @@ -/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ /* SPDX-License-Identifier: Apache-2.0 */ -#include "board/board.h" -#include -#include -#include -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "pbl/util/size.h" +#include -static bool s_protected; -static FlashAddress s_protected_start; -static FlashAddress s_protected_end; +#include "pbl/util/size.h" -static const uint32_t prv_sec_regs[] = { - 0x00002000, - 0x00003000, +static const uint32_t s_sec_reg_addrs[] = { + 0x00002000, + 0x00003000, }; -static QSPIFlashPart QSPI_FLASH_PART = { - .instructions = +const struct pbl_flash_nor_part pbl_flash_nor_gd25lq255e = { + .name = "GD25LQ255E", + .id = 0x1960c8, + .size = 0x2000000, + .cmd = { - .fast_read = 0x0B, - .read2o = 0x3B, - .read2io = 0xBB, - .read4o = 0x6B, - .read4io = 0xEB, - .pp = 0x02, - .pp4o = 0x32, - .erase_sector_4k = 0x20, - .erase_block_64k = 0xD8, .write_enable = 0x06, - .write_disable = 0x04, .rdsr1 = 0x05, .rdsr2 = 0x35, .wrsr = 0x01, @@ -40,193 +24,28 @@ static QSPIFlashPart QSPI_FLASH_PART = { .erase_resume = 0x7A, .enter_low_power = 0xB9, .exit_low_power = 0xAB, - .enter_quad_mode = 0x38, .reset_enable = 0x66, .reset = 0x99, - .qspi_id = 0x9F, /* single SPI ID */ + .read_id = 0x9F, .en4b = 0xB7, .erase_sec = 0x44, .program_sec = 0x42, .read_sec = 0x48, }, - .status_bit_masks = - { - .busy = 1 << 0, - .write_enable = 1 << 1, - }, - .flag_status_bit_masks = - { - .sec_lock = (1 << 5) | (1 << 4), /* SR2, page 12 */ - .erase_suspend = 1 << 7, /* SR2 SUS1, page 14 */ - }, - .dummy_cycles = + .mask = { - .fast_read = 4, + .sr1_busy = 1 << 0, + .sr2_erase_suspend = 1 << 7, }, - .sec_registers = { - .sec_regs = prv_sec_regs, - .num_sec_regs = ARRAY_LENGTH(prv_sec_regs), - .sec_reg_size = 1024, - }, - .supports_block_lock = false, .reset_latency_ms = 12, .suspend_to_read_latency_us = 20, .standby_to_low_power_latency_us = 3, .low_power_to_standby_latency_us = 20, - .supports_fast_read_ddr = false, - .qer_type = JESD216_DW15_QER_S2B1v1, - .qspi_id_value = 0x1960c8, - .size = 0x2000000, /* 32MB */ - .name = "GD25LQ255E", + .qer = PBL_FLASH_NOR_QER_S2B1v1, + .sec_regs = + { + .addrs = s_sec_reg_addrs, + .count = ARRAY_LENGTH(s_sec_reg_addrs), + .size = 1024, + }, }; - -static status_t prv_flash_check_protected(FlashAddress addr) { - if (!s_protected) { - return S_SUCCESS; - } - - if (addr < s_protected_start || addr > s_protected_end) { - return S_SUCCESS; - } - - return E_INVALID_OPERATION; -} - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - return (addr & SECTOR_ADDR_MASK); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return (addr & SUBSECTOR_ADDR_MASK); -} - -void flash_impl_enable_write_protection(void) {} - -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { - if (s_protected) { - return E_ERROR; - } - - s_protected_start = start_sector; - s_protected_end = end_sector; - s_protected = true; - - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { - if (!s_protected) { - return E_ERROR; - } - - s_protected = false; - - return S_SUCCESS; -} - -status_t flash_impl_init(bool coredump_mode) { - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, coredump_mode); - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { return qspi_flash_is_erase_complete(QSPI_FLASH); } - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - status_t status; - - status = prv_flash_check_protected(subsector_addr); - if (FAILED(status)) { - return status; - } - - return qspi_flash_erase_begin(QSPI_FLASH, subsector_addr, true /* is_subsector */); -} -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - status_t status; - - status = prv_flash_check_protected(sector_addr); - if (FAILED(status)) { - return status; - } - - return qspi_flash_erase_begin(QSPI_FLASH, sector_addr, false /* !is_subsector */); -} - -status_t flash_impl_erase_suspend(FlashAddress sector_addr) { - return qspi_flash_erase_suspend(QSPI_FLASH, sector_addr); -} - -status_t flash_impl_erase_resume(FlashAddress sector_addr) { - qspi_flash_erase_resume(QSPI_FLASH, sector_addr); - return S_SUCCESS; -} - -status_t flash_impl_read_sync(void *buffer_ptr, FlashAddress start_addr, size_t buffer_size) { - PBL_ASSERT(buffer_size > 0, "flash_impl_read_sync() called with 0 bytes to read"); - qspi_flash_read_blocking(QSPI_FLASH, start_addr, buffer_ptr, buffer_size); - return S_SUCCESS; -} - -int flash_impl_write_page_begin(const void *buffer, const FlashAddress start_addr, size_t len) { - status_t status; - - status = prv_flash_check_protected(start_addr); - if (FAILED(status)) { - return status; - } - - return qspi_flash_write_page_begin(QSPI_FLASH, buffer, start_addr, len); -} - -status_t flash_impl_get_write_status(void) { return qspi_flash_get_write_status(QSPI_FLASH); } - -status_t flash_impl_enter_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, true); - return S_SUCCESS; -} -status_t flash_impl_exit_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, false); - return S_SUCCESS; -} - -status_t flash_impl_set_burst_mode(bool burst_mode) { - // NYI - return S_SUCCESS; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, false /* !is_subsector */); -} -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, true /* is_subsector */); -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { return 150; } - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { return 50; } - -status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { - return qspi_flash_read_security_register(QSPI_FLASH, addr, val); -} - -status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { - return qspi_flash_security_register_is_locked(QSPI_FLASH, address, locked); -} - -status_t flash_impl_erase_security_register(uint32_t addr) { - return qspi_flash_erase_security_register(QSPI_FLASH, addr); -} - -status_t flash_impl_write_security_register(uint32_t addr, uint8_t val) { - return qspi_flash_write_security_register(QSPI_FLASH, addr, val); -} - -const FlashSecurityRegisters *flash_impl_security_registers_info(void) { - return qspi_flash_security_registers_info(QSPI_FLASH); -} - -#ifdef CONFIG_RECOVERY_FW -status_t flash_impl_lock_security_register(uint32_t address) { - return qspi_flash_lock_security_register(QSPI_FLASH, address); -} -#endif \ No newline at end of file diff --git a/src/fw/drivers/flash/gd25q256e.c b/src/fw/drivers/flash/gd25q256e.c index 950b4d0c67..733e42340a 100644 --- a/src/fw/drivers/flash/gd25q256e.c +++ b/src/fw/drivers/flash/gd25q256e.c @@ -1,39 +1,23 @@ -/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ /* SPDX-License-Identifier: Apache-2.0 */ -#include "board/board.h" -#include -#include -#include -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "pbl/util/size.h" +#include -static bool s_protected; -static FlashAddress s_protected_start; -static FlashAddress s_protected_end; +#include "pbl/util/size.h" -static const uint32_t prv_sec_regs[] = { - 0x00001000, - 0x00002000, - 0x00003000, +static const uint32_t s_sec_reg_addrs[] = { + 0x00001000, + 0x00002000, + 0x00003000, }; -static QSPIFlashPart QSPI_FLASH_PART = { - .instructions = +const struct pbl_flash_nor_part pbl_flash_nor_gd25q256e = { + .name = "GD25Q256E", + .id = 0x1940c8, + .size = 0x2000000, + .cmd = { - .fast_read = 0x0B, - .read2o = 0x3B, - .read2io = 0xBB, - .read4o = 0x6B, - .read4io = 0xEB, - .pp = 0x02, - .pp4o = 0x32, - .erase_sector_4k = 0x20, - .erase_block_64k = 0xD8, .write_enable = 0x06, - .write_disable = 0x04, .rdsr1 = 0x05, .rdsr2 = 0x35, .wrsr = 0x01, @@ -43,190 +27,26 @@ static QSPIFlashPart QSPI_FLASH_PART = { .exit_low_power = 0xAB, .reset_enable = 0x66, .reset = 0x99, - .qspi_id = 0x9F, /* single SPI ID */ + .read_id = 0x9F, .en4b = 0xB7, .erase_sec = 0x44, .program_sec = 0x42, .read_sec = 0x48, }, - .status_bit_masks = - { - .busy = 1 << 0, - .write_enable = 1 << 1, - }, - .flag_status_bit_masks = - { - .sec_lock = (1 << 5) | (1 << 4) | (1 << 3), /* SR2, page 12 */ - .erase_suspend = 1 << 7, /* SR2 SUS1, page 14 */ - }, - .dummy_cycles = + .mask = { - .fast_read = 4, + .sr1_busy = 1 << 0, + .sr2_erase_suspend = 1 << 7, }, - .sec_registers = { - .sec_regs = prv_sec_regs, - .num_sec_regs = ARRAY_LENGTH(prv_sec_regs), - .sec_reg_size = 1024, - }, - .supports_block_lock = false, .reset_latency_ms = 12, .suspend_to_read_latency_us = 20, .standby_to_low_power_latency_us = 3, .low_power_to_standby_latency_us = 20, - .supports_fast_read_ddr = false, - .qer_type = JESD216_DW15_QER_S2B1v1, - .qspi_id_value = 0x1940c8, - .size = 0x2000000, /* 32MB */ - .name = "GD25Q256E", + .qer = PBL_FLASH_NOR_QER_S2B1v1, + .sec_regs = + { + .addrs = s_sec_reg_addrs, + .count = ARRAY_LENGTH(s_sec_reg_addrs), + .size = 1024, + }, }; - -static status_t prv_flash_check_protected(FlashAddress addr) { - if (!s_protected) { - return S_SUCCESS; - } - - if (addr < s_protected_start || addr > s_protected_end) { - return S_SUCCESS; - } - - return E_INVALID_OPERATION; -} - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - return (addr & SECTOR_ADDR_MASK); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return (addr & SUBSECTOR_ADDR_MASK); -} - -void flash_impl_enable_write_protection(void) {} - -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { - if (s_protected) { - return E_ERROR; - } - - s_protected_start = start_sector; - s_protected_end = end_sector; - s_protected = true; - - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { - if (!s_protected) { - return E_ERROR; - } - - s_protected = false; - - return S_SUCCESS; -} - -status_t flash_impl_init(bool coredump_mode) { - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, coredump_mode); - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { return qspi_flash_is_erase_complete(QSPI_FLASH); } - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - status_t status; - - status = prv_flash_check_protected(subsector_addr); - if (FAILED(status)) { - return status; - } - - return qspi_flash_erase_begin(QSPI_FLASH, subsector_addr, true /* is_subsector */); -} -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - status_t status; - - status = prv_flash_check_protected(sector_addr); - if (FAILED(status)) { - return status; - } - - return qspi_flash_erase_begin(QSPI_FLASH, sector_addr, false /* !is_subsector */); -} - -status_t flash_impl_erase_suspend(FlashAddress sector_addr) { - return qspi_flash_erase_suspend(QSPI_FLASH, sector_addr); -} - -status_t flash_impl_erase_resume(FlashAddress sector_addr) { - qspi_flash_erase_resume(QSPI_FLASH, sector_addr); - return S_SUCCESS; -} - -status_t flash_impl_read_sync(void *buffer_ptr, FlashAddress start_addr, size_t buffer_size) { - PBL_ASSERT(buffer_size > 0, "flash_impl_read_sync() called with 0 bytes to read"); - qspi_flash_read_blocking(QSPI_FLASH, start_addr, buffer_ptr, buffer_size); - return S_SUCCESS; -} - -int flash_impl_write_page_begin(const void *buffer, const FlashAddress start_addr, size_t len) { - status_t status; - - status = prv_flash_check_protected(start_addr); - if (FAILED(status)) { - return status; - } - - return qspi_flash_write_page_begin(QSPI_FLASH, buffer, start_addr, len); -} - -status_t flash_impl_get_write_status(void) { return qspi_flash_get_write_status(QSPI_FLASH); } - -status_t flash_impl_enter_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, true); - return S_SUCCESS; -} -status_t flash_impl_exit_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, false); - return S_SUCCESS; -} - -status_t flash_impl_set_burst_mode(bool burst_mode) { - // NYI - return S_SUCCESS; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, false /* !is_subsector */); -} -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, true /* is_subsector */); -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { return 150; } - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { return 50; } - -status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { - return qspi_flash_read_security_register(QSPI_FLASH, addr, val); -} - -status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { - return qspi_flash_security_register_is_locked(QSPI_FLASH, address, locked); -} - -status_t flash_impl_erase_security_register(uint32_t addr) { - return qspi_flash_erase_security_register(QSPI_FLASH, addr); -} - -status_t flash_impl_write_security_register(uint32_t addr, uint8_t val) { - return qspi_flash_write_security_register(QSPI_FLASH, addr, val); -} - -const FlashSecurityRegisters *flash_impl_security_registers_info(void) { - return qspi_flash_security_registers_info(QSPI_FLASH); -} - -#ifdef CONFIG_RECOVERY_FW -status_t flash_impl_lock_security_register(uint32_t address) { - return qspi_flash_lock_security_register(QSPI_FLASH, address); -} -#endif \ No newline at end of file diff --git a/src/fw/drivers/flash/nrf5_qspi.c b/src/fw/drivers/flash/nrf5_qspi.c new file mode 100644 index 0000000000..0a9a5c1d62 --- /dev/null +++ b/src/fw/drivers/flash/nrf5_qspi.c @@ -0,0 +1,652 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include +#include +#include + +#include +#include "kernel/util/delay.h" +#include "kernel/util/sleep.h" +#include "pbl/kernel/sem.h" +#include "pbl/soc/nrf/sleep.h" +#include "pbl/util/math.h" +#include "pbl/util/misc.h" +#include "system/passert.h" + +#include +#include + +PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); + +// NOTE: This driver does not cover anomaly 244, which may cause data corruption +// if HF clock source is switching between HFXO and HFINT (e.g. by BLE). This +// issue has not been observed at operating speeds <= 8MHz, therefore, no +// workaround is implemented here. A warning log will be emitted if driver is +// initialized at higher frequencies. + +// Asynchronous read/write adds overhead for small sizes due to context +// switching and semaphore handling, so we define a minimum size for async ops. +#define MIN_RW_ASYNC_SIZE 256U + +#define PAGE_SIZE 256U + +// Bits 15-12 of a security register address are its one-based index. +#define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) + +// Minimum size to enable 4-byte addressing +#define ADDR_4BYTE_THRESHOLD 0x1000000UL + +static uint8_t __attribute__((aligned(4))) s_bounce_buf[32]; +static struct pbl_flash_nrf5_qspi_state *s_state; + +static inline const struct pbl_flash_nrf5_qspi *prv_cfg(const struct pbl_flash_device *dev) { + return container_of(dev, const struct pbl_flash_nrf5_qspi, dev); +} + +static inline struct pbl_flash_nrf5_qspi_state *prv_state(const struct pbl_flash_device *dev) { + return container_of(dev->state, struct pbl_flash_nrf5_qspi_state, flash); +} + +void QSPI_IRQHandler(void) { + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + pbl_sem_give(&s_state->sem); +} + +// Workaround for nRF52 Anomaly 215: trigger TASKS_ACTIVATE and wait for +// EVENTS_READY before accessing any QSPI register with an offset above 0x600. +static void prv_workaround_215_apply(void) { + nrf_qspi_pins_t pins; + nrf_qspi_pins_t disconnected_pins = { + .sck_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .csn_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io0_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io1_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io2_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io3_pin = NRF_QSPI_PIN_NOT_CONNECTED, + }; + + // Disconnect pins to not wait for response from external memory + nrf_qspi_pins_get(NRF_QSPI, &pins); + nrf_qspi_pins_set(NRF_QSPI, &disconnected_pins); + + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + + nrf_qspi_pins_set(NRF_QSPI, &pins); +} + +static void prv_cinstr_write_read(uint8_t instr, const void *data, void *buf, size_t len) { + nrf_qspi_cinstr_conf_t conf = { + .opcode = instr, + .length = len + 1U, + .io2_level = true, + .io3_level = true, + }; + + PBL_ASSERTN(len <= 8U); + + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + prv_workaround_215_apply(); + if (data != NULL) { + nrf_qspi_cinstrdata_set(NRF_QSPI, conf.length, data); + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_cinstr_transfer_start(NRF_QSPI, &conf); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + if (buf != NULL) { + nrf_qspi_cinstrdata_get(NRF_QSPI, conf.length, buf); + } +} + +static inline void prv_cinstr(uint8_t instr) { + prv_cinstr_write_read(instr, NULL, NULL, 0U); +} + +static inline void prv_cinstr_read(uint8_t instr, void *buf, size_t len) { + prv_cinstr_write_read(instr, NULL, buf, len); +} + +static inline void prv_cinstr_write(uint8_t instr, const void *data, size_t len) { + prv_cinstr_write_read(instr, data, NULL, len); +} + +static bool prv_use_irq(const struct pbl_flash_device *dev, size_t len) { + return len > MIN_RW_ASYNC_SIZE && !dev->state->coredump; +} + +static void prv_xfer_begin(const struct pbl_flash_device *dev, size_t len) { + if (prv_use_irq(dev, len)) { + nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + soc_nrf_sleep_full_block(); + } else { + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); +} + +static void prv_xfer_wait(const struct pbl_flash_device *dev, size_t len) { + if (prv_use_irq(dev, len)) { + pbl_sem_take(&prv_state(dev)->sem, PBL_FOREVER); + soc_nrf_sleep_full_release(); + } else { + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + } +} + +static void prv_read(const struct pbl_flash_device *dev, void *buf, size_t len, uint32_t addr) { + prv_xfer_begin(dev, len); + nrf_qspi_read_buffer_set(NRF_QSPI, buf, len, addr); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_READSTART); + prv_xfer_wait(dev, len); +} + +static void prv_write(const struct pbl_flash_device *dev, const void *buf, size_t len, + uint32_t addr) { + prv_xfer_begin(dev, len); + nrf_qspi_write_buffer_set(NRF_QSPI, buf, len, addr); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_WRITESTART); + prv_xfer_wait(dev, len); +} + +static inline void prv_read_sr1(const struct pbl_flash_nor_part *part, uint8_t *sr1) { + prv_cinstr_read(part->cmd.rdsr1, sr1, 1U); +} + +static inline void prv_read_sr2(const struct pbl_flash_nor_part *part, uint8_t *sr2) { + prv_cinstr_read(part->cmd.rdsr2, sr2, 1U); +} + +static inline bool prv_busy(const struct pbl_flash_nor_part *part) { + uint8_t sr1; + + prv_read_sr1(part, &sr1); + return (sr1 & part->mask.sr1_busy) != 0U; +} + +static inline void prv_wait_idle(const struct pbl_flash_nor_part *part) { + while (prv_busy(part)) { + } +} + +static void prv_configure_qe(const struct pbl_flash_nrf5_qspi *cfg) { + const struct pbl_flash_nor_part *part = cfg->part; + uint8_t sr[2]; + + if (!(cfg->read_mode == PBL_FLASH_NRF5_QSPI_READ_READ2IO || + cfg->read_mode == PBL_FLASH_NRF5_QSPI_READ_READ4O || + cfg->read_mode == PBL_FLASH_NRF5_QSPI_READ_READ4IO || + cfg->write_mode == PBL_FLASH_NRF5_QSPI_WRITE_PP4O || + cfg->write_mode == PBL_FLASH_NRF5_QSPI_WRITE_PP4IO)) { + return; + } + + switch (part->qer) { + case PBL_FLASH_NOR_QER_NONE: + break; + case PBL_FLASH_NOR_QER_S1B6: + prv_read_sr1(part, &sr[0]); + sr[0] |= (1U << 6U); + prv_cinstr_write(part->cmd.wrsr, sr, 1U); + break; + case PBL_FLASH_NOR_QER_S2B1v1: + case PBL_FLASH_NOR_QER_S2B1v4: + case PBL_FLASH_NOR_QER_S2B1v5: + // Writing SR2 requires writing SR1 as well + prv_read_sr1(part, &sr[0]); + prv_read_sr2(part, &sr[1]); + sr[1] |= (1U << 1U); + prv_cinstr_write(part->cmd.wrsr, sr, 2U); + break; + case PBL_FLASH_NOR_QER_S2B1v6: + prv_read_sr2(part, &sr[1]); + sr[1] |= (1U << 1U); + prv_cinstr_write(part->cmd.wrsr2, &sr[1], 1U); + break; + default: + PBL_ASSERTN(false); + } +} + +static int prv_sec_reg_check(const struct pbl_flash_nor_part *part, uint32_t addr) { + for (uint8_t i = 0U; i < part->sec_regs.count; ++i) { + if (addr >= part->sec_regs.addrs[i] && addr < part->sec_regs.addrs[i] + part->sec_regs.size) { + return 0; + } + } + + return -EINVAL; +} + +// Serialises a 3 or 4 byte address plus optional data into a command payload. +static size_t prv_sec_reg_payload(const struct pbl_flash_nor_part *part, uint32_t addr, + uint8_t *out) { + size_t len = 0; + + if (part->size > ADDR_4BYTE_THRESHOLD) { + out[len++] = (addr >> 24U) & 0xFFU; + } + out[len++] = (addr >> 16U) & 0xFFU; + out[len++] = (addr >> 8U) & 0xFFU; + out[len++] = addr & 0xFFU; + + return len; +} + +static int prv_init(const struct pbl_flash_device *dev) { + const struct pbl_flash_nrf5_qspi *cfg = prv_cfg(dev); + const struct pbl_flash_nor_part *part = cfg->part; + struct pbl_flash_nrf5_qspi_state *state = prv_state(dev); + nrf_qspi_pins_t conf_pins; + nrf_qspi_prot_conf_t conf_prot; + nrf_qspi_phy_conf_t conf_phy; + + if (state->initialized) { + return 0; + } + + // QSPI clock is 32MHz, we have dividers from 1 to 16 + PBL_ASSERTN(cfg->clk_freq_hz <= 32000000UL && cfg->clk_freq_hz >= 200000UL); + if (cfg->clk_freq_hz > 8000000UL) { + PBL_LOG_WRN( + "QSPI initialized at %lu Hz, which may cause data corruption if HF clock source switches " + "(anomaly 244)", + cfg->clk_freq_hz); + } + + conf_pins.sck_pin = cfg->clk_gpio; + conf_pins.csn_pin = cfg->cs_gpio; + conf_pins.io0_pin = cfg->data_gpio[0]; + conf_pins.io1_pin = cfg->data_gpio[1]; + conf_pins.io2_pin = cfg->data_gpio[2]; + conf_pins.io3_pin = cfg->data_gpio[3]; + nrf_qspi_pins_set(NRF_QSPI, &conf_pins); + + switch (cfg->read_mode) { + case PBL_FLASH_NRF5_QSPI_READ_READ2O: + conf_prot.readoc = NRF_QSPI_READOC_READ2O; + break; + case PBL_FLASH_NRF5_QSPI_READ_READ2IO: + conf_prot.readoc = NRF_QSPI_READOC_READ2IO; + break; + case PBL_FLASH_NRF5_QSPI_READ_READ4O: + conf_prot.readoc = NRF_QSPI_READOC_READ4O; + break; + case PBL_FLASH_NRF5_QSPI_READ_READ4IO: + conf_prot.readoc = NRF_QSPI_READOC_READ4IO; + break; + default: + conf_prot.readoc = NRF_QSPI_READOC_FASTREAD; + break; + } + + switch (cfg->write_mode) { + case PBL_FLASH_NRF5_QSPI_WRITE_PP2O: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP2O; + break; + case PBL_FLASH_NRF5_QSPI_WRITE_PP4O: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4O; + break; + case PBL_FLASH_NRF5_QSPI_WRITE_PP4IO: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4IO; + break; + default: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP; + break; + } + + if (part->size > ADDR_4BYTE_THRESHOLD) { + conf_prot.addrmode = NRF_QSPI_ADDRMODE_32BIT; + } else { + conf_prot.addrmode = NRF_QSPI_ADDRMODE_24BIT; + } + conf_prot.dpmconfig = false; + nrf_qspi_ifconfig0_set(NRF_QSPI, &conf_prot); + + conf_phy.sck_delay = 5U; + conf_phy.dpmen = false; + conf_phy.spi_mode = NRF_QSPI_MODE_0; + conf_phy.sck_freq = (32000000UL / cfg->clk_freq_hz) - 1U; + nrf_qspi_ifconfig1_set(NRF_QSPI, &conf_phy); + + nrf_qspi_enable(NRF_QSPI); + + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + // Reset the flash to stop any program or erase in progress from before reboot + prv_cinstr(part->cmd.reset_enable); + prv_cinstr(part->cmd.reset); + psleep(part->reset_latency_ms); + + uint32_t id = 0; + prv_cinstr_read(part->cmd.read_id, &id, 3U); + if (id != part->id) { + PBL_LOG_ERR("Flash is not %s (id: 0x%06lx)", part->name, id); + } + + if (conf_prot.addrmode == NRF_QSPI_ADDRMODE_32BIT) { + prv_cinstr(part->cmd.en4b); + } + + prv_configure_qe(cfg); + + s_state = state; + pbl_sem_init(&state->sem, 0, 1); + NVIC_SetPriority(QSPI_IRQn, 5); + NVIC_EnableIRQ(QSPI_IRQn); + + state->initialized = true; + + return 0; +} + +// The peripheral needs word aligned buffers and lengths: unaligned head and +// tail bytes go through a bounce word. +static int prv_read_op(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len) { + uint8_t __attribute__((aligned(4))) b_buf[4]; + size_t pre = MIN((4U - ((uintptr_t)buf % 4U)) % 4U, len); + size_t suf = (len - pre) % 4U; + size_t mid = len - pre - suf; + + if (pre != 0U) { + prv_read(dev, b_buf, 4U, addr); + memcpy(buf, b_buf, pre); + addr += pre; + buf = (uint8_t *)buf + pre; + } + + if (mid != 0U) { + prv_read(dev, buf, mid, addr); + addr += mid; + buf = (uint8_t *)buf + mid; + } + + if (suf != 0U) { + prv_read(dev, b_buf, 4U, addr); + memcpy(buf, b_buf, suf); + } + + return 0; +} + +static void prv_write_page(const struct pbl_flash_device *dev, uint32_t addr, const uint8_t *buf, + size_t len) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + uint8_t __attribute__((aligned(4))) b_buf[4]; + size_t pre = MIN((4U - ((uintptr_t)buf % 4U)) % 4U, len); + size_t suf = (len - pre) % 4U; + size_t mid = len - pre - suf; + + prv_cinstr(part->cmd.write_enable); + + if (pre != 0U) { + memset(&b_buf[pre], 0xff, sizeof(b_buf) - pre); + memcpy(b_buf, buf, pre); + prv_write(dev, b_buf, 4U, addr); + addr += pre; + buf += pre; + } + + if (mid != 0U) { + prv_wait_idle(part); + prv_write(dev, buf, mid, addr); + addr += mid; + buf += mid; + } + + if (suf != 0U) { + prv_wait_idle(part); + memset(&b_buf[suf], 0xff, 4U - suf); + memcpy(b_buf, buf, suf); + prv_write(dev, b_buf, 4U, addr); + } + + prv_wait_idle(part); +} + +static int prv_write_op(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, + size_t len) { + const uint8_t *src = buf; + + while (len > 0) { + size_t chunk = MIN(len, PAGE_SIZE - (addr % PAGE_SIZE)); + const uint8_t *data = src; + + // The peripheral can only read from RAM + if (!nrfx_is_in_ram(src)) { + chunk = MIN(chunk, sizeof(s_bounce_buf)); + memcpy(s_bounce_buf, src, chunk); + data = s_bounce_buf; + } + + prv_write_page(dev, addr, data, chunk); + addr += chunk; + src += chunk; + len -= chunk; + } + + return 0; +} + +static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, size_t size) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + nrf_qspi_erase_len_t len; + + if (size == 4096U) { + len = NRF_QSPI_ERASE_LEN_4KB; + } else if (size == 65536U) { + len = NRF_QSPI_ERASE_LEN_64KB; + } else { + return -EINVAL; + } + + prv_cinstr(part->cmd.write_enable); + + nrf_qspi_erase_ptr_set(NRF_QSPI, addr, len); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ERASESTART); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + // Released once the erase is seen to complete + soc_nrf_sleep_full_block(); + + return 0; +} + +static int prv_erase_status(const struct pbl_flash_device *dev) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + uint8_t sr2; + + if (prv_busy(part)) { + return -EBUSY; + } + + prv_read_sr2(part, &sr2); + if ((sr2 & part->mask.sr2_erase_suspend) != 0U) { + return -EAGAIN; + } + + soc_nrf_sleep_full_release(); + + return 0; +} + +static int prv_erase_suspend(const struct pbl_flash_device *dev) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + + if (!prv_busy(part)) { + return 1; + } + + prv_cinstr(part->cmd.erase_suspend); + if (part->suspend_to_read_latency_us) { + delay_us(part->suspend_to_read_latency_us); + } + + return 0; +} + +static int prv_erase_resume(const struct pbl_flash_device *dev) { + prv_cinstr(prv_cfg(dev)->part->cmd.erase_resume); + return 0; +} + +static void prv_power_down(const struct pbl_flash_device *dev) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + + prv_cinstr(part->cmd.enter_low_power); + if (part->standby_to_low_power_latency_us) { + delay_us(part->standby_to_low_power_latency_us); + } + + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_DEACTIVATE); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_disable(NRF_QSPI); +} + +static void prv_power_up(const struct pbl_flash_device *dev) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + + nrf_qspi_enable(NRF_QSPI); + + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + prv_cinstr(part->cmd.exit_low_power); + if (part->low_power_to_standby_latency_us) { + delay_us(part->low_power_to_standby_latency_us); + } +} + +static int prv_sec_reg_read(const struct pbl_flash_device *dev, uint32_t addr, uint8_t *val) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + uint8_t out[6] = {0}; + uint8_t in[6]; + int ret; + + ret = prv_sec_reg_check(part, addr); + if (ret != 0) { + return ret; + } + + // Address, one dummy byte, then the data byte + size_t len = prv_sec_reg_payload(part, addr, out) + 2U; + prv_cinstr_write_read(part->cmd.read_sec, out, in, len); + *val = in[len - 1U]; + + return 0; +} + +static int prv_sec_reg_write(const struct pbl_flash_device *dev, uint32_t addr, uint8_t val) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + uint8_t out[5]; + int ret; + + ret = prv_sec_reg_check(part, addr); + if (ret != 0) { + return ret; + } + + size_t len = prv_sec_reg_payload(part, addr, out); + out[len++] = val; + + prv_cinstr(part->cmd.write_enable); + prv_cinstr_write(part->cmd.program_sec, out, len); + prv_wait_idle(part); + + return 0; +} + +static int prv_sec_reg_erase(const struct pbl_flash_device *dev, uint32_t addr) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + uint8_t out[4]; + int ret; + + ret = prv_sec_reg_check(part, addr); + if (ret != 0) { + return ret; + } + + size_t len = prv_sec_reg_payload(part, addr, out); + + prv_cinstr(part->cmd.write_enable); + prv_cinstr_write(part->cmd.erase_sec, out, len); + prv_wait_idle(part); + + return 0; +} + +static int prv_sec_reg_is_locked(const struct pbl_flash_device *dev, uint32_t addr, bool *locked) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + uint8_t sr2; + int ret; + + ret = prv_sec_reg_check(part, addr); + if (ret != 0) { + return ret; + } + + prv_read_sr2(part, &sr2); + *locked = (sr2 & ((1U << SEC_ADDR_TO_IDX(addr)) << 3U)) != 0U; + + return 0; +} + +#ifdef CONFIG_RECOVERY_FW +static int prv_sec_reg_lock(const struct pbl_flash_device *dev, uint32_t addr) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + uint8_t sr[2]; + int ret; + + ret = prv_sec_reg_check(part, addr); + if (ret != 0) { + return ret; + } + + prv_read_sr1(part, &sr[0]); + prv_read_sr2(part, &sr[1]); + sr[1] |= (1U << SEC_ADDR_TO_IDX(addr)) << 3U; + prv_cinstr_write(part->cmd.wrsr, sr, 2U); + + return 0; +} +#endif + +const struct pbl_flash_ops pbl_flash_nrf5_qspi_ops = { + .init = prv_init, + .read = prv_read_op, + .write = prv_write_op, + .erase_begin = prv_erase_begin, + .erase_status = prv_erase_status, + .erase_suspend = prv_erase_suspend, + .erase_resume = prv_erase_resume, + .power_down = prv_power_down, + .power_up = prv_power_up, + .sec_reg_read = prv_sec_reg_read, + .sec_reg_write = prv_sec_reg_write, + .sec_reg_erase = prv_sec_reg_erase, + .sec_reg_is_locked = prv_sec_reg_is_locked, +#ifdef CONFIG_RECOVERY_FW + .sec_reg_lock = prv_sec_reg_lock, +#endif +}; diff --git a/src/fw/drivers/flash/qemu.c b/src/fw/drivers/flash/qemu.c new file mode 100644 index 0000000000..1180d04f45 --- /dev/null +++ b/src/fw/drivers/flash/qemu.c @@ -0,0 +1,96 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include +#include + +#include "pbl/util/misc.h" + +#define REG32(base, off) (*(volatile uint32_t *)((base) + (off))) + +#define FLASH_CMD 0x00 +#define FLASH_ADDR 0x04 +#define FLASH_STATUS 0x08 +// Persistence handshake: after each page write, QEMU is told the written range +// so that only complete pages (never torn writes) reach the backing file. +// Erases are persisted by QEMU on their own. +#define FLASH_SYNC_LEN 0x18 +#define FLASH_SYNC 0x1C + +#define CMD_ERASE_SUBSECTOR 1 +#define CMD_ERASE_SECTOR 2 + +#define STATUS_BUSY (1 << 0) + +#define PAGE_SIZE 256U +#define SUBSECTOR_SIZE 4096U +#define SECTOR_SIZE 65536U + +static inline uintptr_t prv_regs(const struct pbl_flash_device *dev) { + return container_of(dev, const struct pbl_flash_qemu, dev)->regs; +} + +static int prv_init(const struct pbl_flash_device *dev) { + return 0; +} + +// Flash addresses are XIP addresses, so data can be accessed directly. +static int prv_read(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len) { + memcpy(buf, (const void *)(uintptr_t)addr, len); + return 0; +} + +static int prv_write(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, + size_t len) { + uintptr_t regs = prv_regs(dev); + const uint8_t *src = buf; + + while (len > 0) { + size_t chunk = PAGE_SIZE - (addr % PAGE_SIZE); + if (chunk > len) { + chunk = len; + } + + volatile uint8_t *dst = (volatile uint8_t *)(uintptr_t)addr; + for (size_t i = 0; i < chunk; i++) { + dst[i] = src[i]; + } + REG32(regs, FLASH_SYNC_LEN) = chunk; + REG32(regs, FLASH_SYNC) = addr; + + addr += chunk; + src += chunk; + len -= chunk; + } + + return 0; +} + +static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, size_t size) { + uintptr_t regs = prv_regs(dev); + uint32_t cmd; + + if (size == SECTOR_SIZE) { + cmd = CMD_ERASE_SECTOR; + } else if (size == SUBSECTOR_SIZE) { + cmd = CMD_ERASE_SUBSECTOR; + } else { + return -EINVAL; + } + + REG32(regs, FLASH_ADDR) = addr; + REG32(regs, FLASH_CMD) = cmd; + while (REG32(regs, FLASH_STATUS) & STATUS_BUSY) { + } + + return 0; +} + +const struct pbl_flash_ops pbl_flash_qemu_ops = { + .init = prv_init, + .read = prv_read, + .write = prv_write, + .erase_begin = prv_erase_begin, +}; diff --git a/src/fw/drivers/flash/sf32lb52_mpi.c b/src/fw/drivers/flash/sf32lb52_mpi.c new file mode 100644 index 0000000000..ef725d506a --- /dev/null +++ b/src/fw/drivers/flash/sf32lb52_mpi.c @@ -0,0 +1,284 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include +#include +#include + +#include +#include "kernel/pbl_malloc.h" +#include "pbl/kernel/irq.h" +#include "pbl/mcu/cache.h" +#include "pbl/util/misc.h" +#include "system/passert.h" + +#include + +PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); + +#define PAGE_SIZE 256U +#define SUBSECTOR_SIZE 4096U +#define SECTOR_SIZE 65536U + +// Bits 15-12 of a security register address are its one-based index. +#define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) + +static inline const struct pbl_flash_sf32lb52_mpi *prv_cfg(const struct pbl_flash_device *dev) { + return container_of(dev, const struct pbl_flash_sf32lb52_mpi, dev); +} + +static inline struct pbl_flash_sf32lb52_mpi_state *prv_state(const struct pbl_flash_device *dev) { + return container_of(dev->state, struct pbl_flash_sf32lb52_mpi_state, flash); +} + +static inline FLASH_HandleTypeDef *prv_handle(const struct pbl_flash_device *dev) { + return &prv_state(dev)->ctx.handle; +} + +static int prv_init(const struct pbl_flash_device *dev) { + const struct pbl_flash_sf32lb52_mpi *cfg = prv_cfg(dev); + struct pbl_flash_sf32lb52_mpi_state *state = prv_state(dev); + HAL_StatusTypeDef res; + + if (state->initialized) { + // DMA completion relies on interrupts, which are unavailable in a coredump + state->ctx.handle.dma = dev->state->coredump ? NULL : &state->hdma; + return 0; + } + + state->ctx.dual_mode = 1; + res = HAL_FLASH_Init(&state->ctx, &state->cfg, &state->hdma, &state->dma, cfg->clk_div); + PBL_ASSERT(res == HAL_OK, "HAL_FLASH_Init failed"); + + if (state->ctx.dev_id != cfg->id) { + PBL_LOG_ERR("Flash is not %s (id: 0x%" PRIx32 ")", cfg->name, state->ctx.dev_id); + } + + state->initialized = true; + + return 0; +} + +static int prv_read(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len) { + memcpy(buf, (const void *)addr, len); + return 0; +} + +static int prv_write(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, + size_t len) { + FLASH_HandleTypeDef *hflash = prv_handle(dev); + uint8_t *local_buf = NULL; + const uint8_t *src = buf; + int ret = 0; + + if (addr < hflash->base || addr > hflash->base + hflash->size) { + return -EINVAL; + } + + // The source must be reachable by the DMA and not live in the flash itself + if (IS_SAME_FLASH_ADDR(src, addr) || IS_SPI_NONDMA_RAM_ADDR(src) || + IS_DMA_ACCROSS_1M_BOUNDARY((uint32_t)src, len)) { + local_buf = kernel_malloc_check(len); + memcpy(local_buf, src, len); + src = local_buf; + } + + // The DMA bypasses the D-cache + uintptr_t flush_addr = (uintptr_t)src; + size_t flush_size = len; + dcache_align(&flush_addr, &flush_size); + dcache_flush((const void *)flush_addr, flush_size); + + uint32_t offset = addr - hflash->base; + size_t remain = len; + + while (remain > 0) { + size_t chunk = PAGE_SIZE - (offset % PAGE_SIZE); + if (chunk > remain) { + chunk = remain; + } + + pbl_irq_lock(); + int res = HAL_QSPIEX_WRITE_PAGE(hflash, offset, (uint8_t *)src, chunk); + pbl_irq_unlock(); + if ((size_t)res != chunk) { + ret = -EIO; + break; + } + + offset += chunk; + src += chunk; + remain -= chunk; + } + + SCB_InvalidateDCache_by_Addr((void *)addr, len); + SCB_InvalidateICache_by_Addr((void *)addr, len); + + if (local_buf != NULL) { + kernel_free(local_buf); + } + + return ret; +} + +static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, size_t size) { + FLASH_HandleTypeDef *hflash = prv_handle(dev); + int res; + + if (addr < hflash->base || addr > hflash->base + hflash->size) { + return -EINVAL; + } + + uint32_t offset = addr - hflash->base; + + pbl_irq_lock(); + if (size == SECTOR_SIZE) { + res = HAL_QSPIEX_BLK64_ERASE(hflash, offset); + } else if (size == SUBSECTOR_SIZE) { + res = HAL_QSPIEX_SECT_ERASE(hflash, offset); + } else { + res = -1; + } + pbl_irq_unlock(); + + SCB_InvalidateDCache_by_Addr((void *)addr, size); + SCB_InvalidateICache_by_Addr((void *)addr, size); + + return (res == 0) ? 0 : -EIO; +} + +static void prv_power_down(const struct pbl_flash_device *dev) { + HAL_FLASH_NOP_CMD(prv_handle(dev)); +} + +void pbl_flash_sf32lb52_mpi_dpd_enter(const struct pbl_flash_device *dev) { + FLASH_HandleTypeDef *hflash = prv_handle(dev); + + HAL_FLASH_NOP_CMD(hflash); + HAL_FLASH_DEEP_PWRDOWN(hflash); + HAL_Delay_us(prv_cfg(dev)->dpd_enter_us); +} + +void pbl_flash_sf32lb52_mpi_dpd_exit(const struct pbl_flash_device *dev) { + HAL_FLASH_RELEASE_DPD(prv_handle(dev)); + HAL_Delay_us(prv_cfg(dev)->dpd_exit_us); +} + +static int prv_sec_reg_check(const struct pbl_flash_device *dev, uint32_t addr) { + const struct pbl_flash_sec_regs *regs = dev->sec_regs; + + if (regs == NULL) { + return -ENOTSUP; + } + + for (uint8_t i = 0U; i < regs->count; ++i) { + if (addr >= regs->addrs[i] && addr < regs->addrs[i] + regs->size) { + return 0; + } + } + + return -EINVAL; +} + +static int prv_sec_reg_read(const struct pbl_flash_device *dev, uint32_t addr, uint8_t *val) { + int ret = prv_sec_reg_check(dev, addr); + if (ret != 0) { + return ret; + } + + // Reads must be word sized and aligned + uint8_t values[4] = {0}; + uint32_t offset = addr % 4; + + pbl_irq_lock(); + int res = HAL_QSPI_READ_OTP(prv_handle(dev), addr - offset, values, 4); + pbl_irq_unlock(); + if (res != 4) { + return -EIO; + } + + *val = values[offset]; + + return 0; +} + +static int prv_sec_reg_write(const struct pbl_flash_device *dev, uint32_t addr, uint8_t val) { + int ret = prv_sec_reg_check(dev, addr); + if (ret != 0) { + return ret; + } + + uintptr_t flush_addr = (uintptr_t)&val; + size_t flush_size = sizeof(val); + dcache_align(&flush_addr, &flush_size); + dcache_flush((const void *)flush_addr, flush_size); + + pbl_irq_lock(); + int res = HAL_QSPI_WRITE_OTP(prv_handle(dev), addr, &val, 1); + pbl_irq_unlock(); + + return (res == 1) ? 0 : -EIO; +} + +static int prv_sec_reg_erase(const struct pbl_flash_device *dev, uint32_t addr) { + int ret = prv_sec_reg_check(dev, addr); + if (ret != 0) { + return ret; + } + + pbl_irq_lock(); + int res = HAL_QSPI_ERASE_OTP(prv_handle(dev), addr); + pbl_irq_unlock(); + + return (res == 0) ? 0 : -EIO; +} + +static int prv_sec_reg_is_locked(const struct pbl_flash_device *dev, uint32_t addr, bool *locked) { + int ret = prv_sec_reg_check(dev, addr); + if (ret != 0) { + return ret; + } + + pbl_irq_lock(); + uint8_t lb = HAL_QSPI_GET_OTP_LB(prv_handle(dev)); + pbl_irq_unlock(); + if (lb == 0xff) { + return -EIO; + } + + *locked = (lb & (1U << SEC_ADDR_TO_IDX(addr))) != 0U; + + return 0; +} + +#ifdef CONFIG_RECOVERY_FW +static int prv_sec_reg_lock(const struct pbl_flash_device *dev, uint32_t addr) { + int ret = prv_sec_reg_check(dev, addr); + if (ret != 0) { + return ret; + } + + pbl_irq_lock(); + int res = HAL_QSPI_LOCK_OTP(prv_handle(dev), addr); + pbl_irq_unlock(); + + return (res == 0) ? 0 : -EIO; +} +#endif + +const struct pbl_flash_ops pbl_flash_sf32lb52_mpi_ops = { + .init = prv_init, + .read = prv_read, + .write = prv_write, + .erase_begin = prv_erase_begin, + .power_down = prv_power_down, + .sec_reg_read = prv_sec_reg_read, + .sec_reg_write = prv_sec_reg_write, + .sec_reg_erase = prv_sec_reg_erase, + .sec_reg_is_locked = prv_sec_reg_is_locked, +#ifdef CONFIG_RECOVERY_FW + .sec_reg_lock = prv_sec_reg_lock, +#endif +}; diff --git a/src/fw/drivers/nrf5/qspi.c b/src/fw/drivers/nrf5/qspi.c deleted file mode 100644 index b74372042f..0000000000 --- a/src/fw/drivers/nrf5/qspi.c +++ /dev/null @@ -1,786 +0,0 @@ -/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include - -#include "board/board.h" -#include -#include -#include "flash_region/flash_region.h" -#include "kernel/util/delay.h" -#include "kernel/util/sleep.h" -#include "pbl/soc/nrf/sleep.h" -#include -#include "system/passert.h" -#include "pbl/util/math.h" - -#include -#include - -#include "pbl/kernel/sem.h" - -// NOTE: This driver does not cover anomaly 244, which may cause data corruption -// if HF clock source is switching between HFXO and HFINT (e.g. by BLE). This -// issue has not been observed at operating speeds <= 8MHz, therefore, no -// workaround is implemented here. A warning log will be emitted if driver is -// initialized at higher frequencies. - -// Asynchronous read/write can actually add overhead for small sizes due to context -// switching and semaphore handling, so we define a minimum size for async ops. -#define MIN_RW_ASYNC_SIZE 256U - -// Obtain security register index (zero-indexed) from address -// Bits 15-12 of the address correspond to the security register index (one-indexed) -#define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) - -// Minimum address to enable 4-byte addressing -#define ADDR_4BYTE_THRESHOLD 0x1000000UL - -static uint8_t __attribute__((aligned(4))) s_bounce_buf[32]; - -void QSPI_IRQHandler(void) { - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - - pbl_sem_give(&QSPI_FLASH->qspi->state->sem); -} - -// ----------------------------------------------------------------------------- -// Internal helpers - -// Workaround for nRF52 Anomaly 215: -// -// Symptoms: -// CPU halts. -// -// Conditions: -// Init and start QSPI, use XIP, then write to or read any QSPI register with an -// offset above 0x600. -// -// Consequences: -// CPU halts. -// -// Workaround: -// Trigger QSPI TASKS_ACTIVATE after XIP is used and wait for QSPI EVENTS_READY -// before accessing any QSPI register with an offset above 0x600. -static void prv_workaround_215_apply(void) { - nrf_qspi_pins_t pins; - nrf_qspi_pins_t disconnected_pins = { - .sck_pin = NRF_QSPI_PIN_NOT_CONNECTED, - .csn_pin = NRF_QSPI_PIN_NOT_CONNECTED, - .io0_pin = NRF_QSPI_PIN_NOT_CONNECTED, - .io1_pin = NRF_QSPI_PIN_NOT_CONNECTED, - .io2_pin = NRF_QSPI_PIN_NOT_CONNECTED, - .io3_pin = NRF_QSPI_PIN_NOT_CONNECTED, - }; - - // Disconnect pins to not wait for response from external memory - nrf_qspi_pins_get(NRF_QSPI, &pins); - nrf_qspi_pins_set(NRF_QSPI, &disconnected_pins); - - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); - - while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { - } - - // Restore previous pins - nrf_qspi_pins_set(NRF_QSPI, &pins); -} - -static void prv_cinstr_write_read(QSPIFlash *dev, uint8_t instr, const void *data, void *buf, - size_t len) { - nrf_qspi_cinstr_conf_t conf = { - .opcode = instr, - .length = len + 1U, - .io2_level = true, - .io3_level = true, - }; - - PBL_ASSERTN(len <= 8U); - - nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); - // Accessing registers with offset > 0x600 below, requires workaround for Anomaly 215 - prv_workaround_215_apply(); - if (data != NULL) { - nrf_qspi_cinstrdata_set(NRF_QSPI, conf.length, data); - } - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - nrf_qspi_cinstr_transfer_start(NRF_QSPI, &conf); - while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { - } - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - - if (buf != NULL) { - nrf_qspi_cinstrdata_get(NRF_QSPI, conf.length, buf); - } -} - -static inline void prv_cinstr(QSPIFlash *dev, uint8_t instr) { - prv_cinstr_write_read(dev, instr, NULL, NULL, 0U); -} - -static inline void prv_cinstr_read(QSPIFlash *dev, uint8_t instr, void *buf, size_t len) { - prv_cinstr_write_read(dev, instr, NULL, buf, len); -} - -static inline void prv_cinstr_write(QSPIFlash *dev, uint8_t instr, const void *data, size_t len) { - prv_cinstr_write_read(dev, instr, data, NULL, len); -} - -static void prv_read(QSPIFlash *dev, void *buf, size_t len, uint32_t addr) { - if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { - nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); - } else { - nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); - soc_nrf_sleep_full_block(); - } - - nrf_qspi_read_buffer_set(NRF_QSPI, buf, len, addr); - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_READSTART); - - if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { - while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { - } - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - } else { - pbl_sem_take(&dev->qspi->state->sem, PBL_FOREVER); - soc_nrf_sleep_full_release(); - } -} - -static void prv_write(QSPIFlash *dev, const void *buf, size_t len, uint32_t addr) { - if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { - nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); - } else { - nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); - soc_nrf_sleep_full_block(); - } - - nrf_qspi_write_buffer_set(NRF_QSPI, buf, len, addr); - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_WRITESTART); - - if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { - while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { - } - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - } else { - pbl_sem_take(&dev->qspi->state->sem, PBL_FOREVER); - soc_nrf_sleep_full_release(); - } -} - -static inline void prv_write_enable(QSPIFlash *dev) { - QSPIFlashPart *part = dev->state->part; - - prv_cinstr(dev, part->instructions.write_enable); -} - -static inline void prv_read_sr1(QSPIFlash *dev, uint8_t *sr1) { - QSPIFlashPart *part = dev->state->part; - - prv_cinstr_read(dev, part->instructions.rdsr1, sr1, 1U); -} - -static inline void prv_read_sr2(QSPIFlash *dev, uint8_t *sr2) { - QSPIFlashPart *part = dev->state->part; - - prv_cinstr_read(dev, part->instructions.rdsr2, sr2, 1U); -} - -static inline void prv_write_sr(QSPIFlash *dev, const uint8_t *sr, size_t len) { - QSPIFlashPart *part = dev->state->part; - - prv_cinstr_write(dev, part->instructions.wrsr, sr, len); -} - -static inline void prv_write_sr2(QSPIFlash *dev, uint8_t sr2) { - QSPIFlashPart *part = dev->state->part; - - prv_cinstr_write(dev, part->instructions.wrsr2, &sr2, 1U); -} - -static inline bool prv_wip_check(QSPIFlash *dev) { - QSPIFlashPart *part = dev->state->part; - uint8_t sr1; - - prv_read_sr1(dev, &sr1); - - return (sr1 & part->status_bit_masks.busy) != 0U; -} - -static void prv_configure_qe(QSPIFlash *dev) { - QSPIFlashPart *part = dev->state->part; - uint8_t sr[2]; - - // Check first if read/write mode requires QE to be set - if (!(dev->read_mode == QSPI_FLASH_READ_READ2IO || dev->read_mode == QSPI_FLASH_READ_READ4O || - dev->read_mode == QSPI_FLASH_READ_READ4IO || dev->write_mode == QSPI_FLASH_WRITE_PP4O || - dev->write_mode == QSPI_FLASH_WRITE_PP4IO)) { - return; - } - - // Check if QE is needed - if (part->qer_type == JESD216_DW15_QER_NONE) { - return; - } - - // Enable QE bit - switch (part->qer_type) { - case JESD216_DW15_QER_S1B6: - prv_read_sr1(dev, &sr[0]); - sr[0] |= (1U << 6U); - prv_write_sr(dev, sr, 1U); - break; - case JESD216_DW15_QER_S2B1v1: - case JESD216_DW15_QER_S2B1v4: - case JESD216_DW15_QER_S2B1v5: - // Writing SR2 requires writing SR1 as well - prv_read_sr1(dev, &sr[0]); - prv_read_sr2(dev, &sr[1]); - sr[1] |= (1U << 1U); - prv_write_sr(dev, sr, 2U); - break; - case JESD216_DW15_QER_S2B1v6: - // We can write SR2 without writing SR1 - prv_read_sr2(dev, &sr[1]); - sr[1] |= (1U << 1U); - prv_write_sr2(dev, sr[1]); - break; - default: - PBL_ASSERTN(false); - } -} - -status_t prv_qspi_security_register_check(QSPIFlash *dev, uint32_t addr) { - QSPIFlashPart *part = dev->state->part; - bool addr_valid = false; - - for (uint8_t i = 0U; i < part->sec_registers.num_sec_regs; ++i) { - if (addr >= part->sec_registers.sec_regs[i] && - addr < part->sec_registers.sec_regs[i] + part->sec_registers.sec_reg_size) { - addr_valid = true; - break; - } - } - - if (!addr_valid) { - return E_INVALID_ARGUMENT; - } - - return S_SUCCESS; -} - -// ----------------------------------------------------------------------------- -// QSPI interface - -void qspi_flash_init(QSPIFlash *dev, QSPIFlashPart *part, bool coredump_mode) { - nrf_qspi_pins_t conf_pins; - nrf_qspi_prot_conf_t conf_prot; - nrf_qspi_phy_conf_t conf_phy; - - dev->state->part = part; - dev->state->coredump_mode = coredump_mode; - - if (dev->qspi->state->initialized) { - return; - } - - // QSPI clock is 32MHz, we have dividers from 1 to 16 - PBL_ASSERTN(dev->qspi->clk_freq_hz <= 32000000UL && dev->qspi->clk_freq_hz >= 200000UL); - if (dev->qspi->clk_freq_hz > 8000000UL) { - PBL_LOG_WRN( - "QSPI initialized at %lu Hz, which may cause data corruption if HF clock source switches " - "(anomaly 244)", - dev->qspi->clk_freq_hz); - } - - // QSPI pins - conf_pins.sck_pin = dev->qspi->clk_gpio; - conf_pins.csn_pin = dev->qspi->cs_gpio; - conf_pins.io0_pin = dev->qspi->data_gpio[0]; - conf_pins.io1_pin = dev->qspi->data_gpio[1]; - conf_pins.io2_pin = dev->qspi->data_gpio[2]; - conf_pins.io3_pin = dev->qspi->data_gpio[3]; - - nrf_qspi_pins_set(NRF_QSPI, &conf_pins); - - // QSPI protocol configuration - switch (dev->read_mode) { - case QSPI_FLASH_READ_READ2O: - conf_prot.readoc = NRF_QSPI_READOC_READ2O; - break; - case QSPI_FLASH_READ_READ2IO: - conf_prot.readoc = NRF_QSPI_READOC_READ2IO; - break; - case QSPI_FLASH_READ_READ4O: - conf_prot.readoc = NRF_QSPI_READOC_READ4O; - break; - case QSPI_FLASH_READ_READ4IO: - conf_prot.readoc = NRF_QSPI_READOC_READ4IO; - break; - default: - conf_prot.readoc = NRF_QSPI_READOC_FASTREAD; - break; - } - - switch (dev->write_mode) { - case QSPI_FLASH_WRITE_PP2O: - conf_prot.writeoc = NRF_QSPI_WRITEOC_PP2O; - break; - case QSPI_FLASH_WRITE_PP4O: - conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4O; - break; - case QSPI_FLASH_WRITE_PP4IO: - conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4IO; - break; - default: - conf_prot.writeoc = NRF_QSPI_WRITEOC_PP; - break; - } - - if (part->size > ADDR_4BYTE_THRESHOLD) { - conf_prot.addrmode = NRF_QSPI_ADDRMODE_32BIT; - } else { - conf_prot.addrmode = NRF_QSPI_ADDRMODE_24BIT; - } - - conf_prot.dpmconfig = false; - - nrf_qspi_ifconfig0_set(NRF_QSPI, &conf_prot); - - // QSPI PHY configuration - conf_phy.sck_delay = 5U; - conf_phy.dpmen = false; - conf_phy.spi_mode = NRF_QSPI_MODE_0; - conf_phy.sck_freq = (32000000UL / dev->qspi->clk_freq_hz) - 1U; - - nrf_qspi_ifconfig1_set(NRF_QSPI, &conf_phy); - - // Enable QSPI peripheral - nrf_qspi_enable(NRF_QSPI); - - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); - while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { - } - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - - // Reset the flash to stop any program's or erase in progress from before reboot - prv_cinstr(dev, part->instructions.reset_enable); - prv_cinstr(dev, part->instructions.reset); - - psleep(part->reset_latency_ms); - - // Enable 4-byte addressing if needed - if (conf_prot.addrmode == NRF_QSPI_ADDRMODE_32BIT) { - prv_cinstr(dev, part->instructions.en4b); - } - - // Configure QE if needed - prv_configure_qe(dev); - - NVIC_SetPriority(QSPI_IRQn, 5); - NVIC_EnableIRQ(QSPI_IRQn); - - pbl_sem_init(&dev->qspi->state->sem, 0, 1); - dev->qspi->state->initialized = true; -} - -bool qspi_flash_check_whoami(QSPIFlash *dev) { - QSPIFlashPart *part = dev->state->part; - uint32_t val; - - prv_cinstr_read(dev, part->instructions.qspi_id, &val, 3U); - - return val == part->qspi_id_value; -} - -bool qspi_flash_is_in_coredump_mode(QSPIFlash *dev) { - return dev->state->coredump_mode; -} - -status_t qspi_flash_erase_begin(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - nrf_qspi_erase_len_t len; - - // address must be word aligned - if ((addr & 0x3U) != 0U) { - return E_INVALID_ARGUMENT; - } - - if (is_subsector) { - len = NRF_QSPI_ERASE_LEN_4KB; - } else { - len = NRF_QSPI_ERASE_LEN_64KB; - } - - prv_write_enable(dev); - - nrf_qspi_erase_ptr_set(NRF_QSPI, addr, len); - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ERASESTART); - while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { - } - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - - soc_nrf_sleep_full_block(); - - return S_SUCCESS; -} - -status_t qspi_flash_erase_suspend(QSPIFlash *dev, uint32_t addr) { - QSPIFlashPart *part = dev->state->part; - - if (!prv_wip_check(dev)) { - return S_NO_ACTION_REQUIRED; - } - - prv_cinstr(dev, part->instructions.erase_suspend); - - if (part->suspend_to_read_latency_us) { - delay_us(part->suspend_to_read_latency_us); - } - - return S_SUCCESS; -} - -void qspi_flash_erase_resume(QSPIFlash *dev, uint32_t addr) { - QSPIFlashPart *part = dev->state->part; - - prv_cinstr(dev, part->instructions.erase_resume); -} - -status_t qspi_flash_is_erase_complete(QSPIFlash *dev) { - QSPIFlashPart *part = dev->state->part; - uint8_t sr2; - - // erase ongoing - if (prv_wip_check(dev)) { - return E_BUSY; - } - - // erase suspended - prv_read_sr2(dev, &sr2); - if ((sr2 & part->flag_status_bit_masks.erase_suspend) != 0U) { - return E_AGAIN; - } - - soc_nrf_sleep_full_release(); - - return S_SUCCESS; -} - -void qspi_flash_read_blocking(QSPIFlash *dev, uint32_t addr, void *buffer, uint32_t length) { - uint8_t __attribute__((aligned(4))) b_buf[4]; - uint8_t buf_pre; - uint8_t buf_suf; - uint32_t buf_mid; - - buf_pre = (4U - (uint8_t)((uint32_t)buffer % 4U)) % 4U; - if (buf_pre > length) { - buf_pre = length; - } - - buf_suf = (uint8_t)((length - buf_pre) % 4U); - buf_mid = length - buf_pre - buf_suf; - - if (buf_pre != 0U) { - prv_read(dev, b_buf, 4U, addr); - - memcpy(buffer, b_buf, buf_pre); - - addr += buf_pre; - buffer = ((uint8_t *)buffer) + buf_pre; - } - - if (buf_mid != 0U) { - prv_read(dev, buffer, buf_mid, addr); - - addr += buf_mid; - buffer = ((uint8_t *)buffer) + buf_mid; - } - - if (buf_suf != 0U) { - prv_read(dev, b_buf, 4U, addr); - - memcpy(buffer, b_buf, buf_suf); - } -} - -int qspi_flash_write_page_begin(QSPIFlash *dev, const void *buffer, uint32_t addr, - uint32_t length) { - uint8_t __attribute__((aligned(4))) b_buf[4]; - uint8_t buf_pre; - uint8_t buf_suf; - uint32_t buf_mid; - - // we can write from start address up to the end of the page - length = MIN(length, PAGE_SIZE_BYTES - (addr % PAGE_SIZE_BYTES)); - - // bounce data to RAM if not in RAM - if (!nrfx_is_in_ram(buffer)) { - length = MIN(length, sizeof(s_bounce_buf)); - memcpy(s_bounce_buf, buffer, length); - buffer = s_bounce_buf; - } - - // buffer needs to be word aligned, if it is not, split the write into chunks - // (prefix/middle/suffix) with word-aligned buffers - buf_pre = (4U - (uint8_t)((uint32_t)buffer % 4U)) % 4U; - if (buf_pre > length) { - buf_pre = length; - } - - buf_suf = (uint8_t)((length - buf_pre) % 4U); - buf_mid = length - buf_pre - buf_suf; - - prv_write_enable(dev); - - if (buf_pre != 0U) { - memset(&b_buf[buf_pre], 0xff, sizeof(b_buf) - buf_pre); - memcpy(b_buf, buffer, buf_pre); - - prv_write(dev, b_buf, 4U, addr); - - addr += buf_pre; - buffer = ((uint8_t *)buffer) + buf_pre; - } - - if (buf_mid != 0U) { - while (prv_wip_check(dev)) { - } - - prv_write(dev, buffer, buf_mid, addr); - - addr += buf_mid; - buffer = ((uint8_t *)buffer) + buf_mid; - } - - if (buf_suf != 0U) { - while (prv_wip_check(dev)) { - } - - memset(&b_buf[buf_suf], 0xff, 4U - buf_suf); - memcpy(b_buf, buffer, buf_suf); - - prv_write(dev, b_buf, 4U, addr); - } - - return length; -} - -status_t qspi_flash_get_write_status(QSPIFlash *dev) { - return prv_wip_check(dev) ? E_BUSY : S_SUCCESS; -} - -void qspi_flash_set_lower_power_mode(QSPIFlash *dev, bool active) { - QSPIFlashPart *part = dev->state->part; - - if (active) { - prv_cinstr(dev, part->instructions.enter_low_power); - if (part->standby_to_low_power_latency_us) { - delay_us(part->standby_to_low_power_latency_us); - } - - nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); - nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_DEACTIVATE); - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - - nrf_qspi_disable(NRF_QSPI); - } else { - nrf_qspi_enable(NRF_QSPI); - - nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); - while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { - } - nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - - prv_cinstr(dev, part->instructions.exit_low_power); - if (part->low_power_to_standby_latency_us) { - delay_us(part->low_power_to_standby_latency_us); - } - } -} - -status_t qspi_flash_blank_check(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - const uint32_t size_bytes = is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES; - const uint32_t BUF_SIZE_BYTES = 128; - const uint32_t BUF_SIZE_WORDS = BUF_SIZE_BYTES / sizeof(uint32_t); - uint32_t buffer[BUF_SIZE_WORDS]; - - for (uint32_t offset = 0U; offset < size_bytes; offset += BUF_SIZE_BYTES) { - qspi_flash_read_blocking(dev, addr + offset, buffer, BUF_SIZE_BYTES); - for (uint32_t i = 0U; i < BUF_SIZE_WORDS; ++i) { - if (buffer[i] != 0xFFFFFFFFUL) { - return S_FALSE; - } - } - } - - return S_TRUE; -} - -status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_t *val) { - QSPIFlashPart *part = dev->state->part; - status_t ret; - uint8_t out[6]; - uint8_t in[6]; - uint8_t len; - - ret = prv_qspi_security_register_check(dev, addr); - if (ret != S_SUCCESS) { - return ret; - } - - if (part->size > ADDR_4BYTE_THRESHOLD) { - len = 6; - out[0] = (addr >> 24U); - out[1] = (addr >> 16U) & 0xFFU; - out[2] = (addr >> 8U) & 0xFFU; - out[3] = addr & 0xFFU; - } else { - len = 5; - out[0] = (addr >> 16U) & 0xFFU; - out[1] = (addr >> 8U) & 0xFFU; - out[2] = addr & 0xFFU; - } - - prv_cinstr_write_read(dev, part->instructions.read_sec, out, in, len); - - if (part->size > ADDR_4BYTE_THRESHOLD) { - *val = in[5]; - } else { - *val = in[4]; - } - - return 0; -} - -status_t qspi_flash_security_register_is_locked(QSPIFlash *dev, uint32_t addr, bool *locked) { - QSPIFlashPart *part = dev->state->part; - uint8_t sr2; - status_t res; - - res = prv_qspi_security_register_check(dev, addr); - if (res != S_SUCCESS) { - return res; - } - - prv_cinstr_read(dev, part->instructions.rdsr2, &sr2, 1); - - *locked = !!(sr2 & ((1U << SEC_ADDR_TO_IDX(addr)) << 3U)); - - return 0; -} - -status_t qspi_flash_erase_security_register(QSPIFlash *dev, uint32_t addr) { - QSPIFlashPart *part = dev->state->part; - status_t ret; - uint8_t out[4]; - uint8_t len; - - ret = prv_qspi_security_register_check(dev, addr); - if (ret != S_SUCCESS) { - return ret; - } - - if (part->size > ADDR_4BYTE_THRESHOLD) { - len = 4U; - out[0] = (addr >> 24U); - out[1] = (addr >> 16U) & 0xFFU; - out[2] = (addr >> 8U) & 0xFFU; - out[3] = addr & 0xFFU; - } else { - len = 3U; - out[0] = (addr >> 16U) & 0xFFU; - out[1] = (addr >> 8U) & 0xFFU; - out[2] = addr & 0xFFU; - } - - prv_write_enable(dev); - - prv_cinstr_write(dev, part->instructions.erase_sec, out, len); - - while (prv_wip_check(dev)) { - } - - return 0; -} - -status_t qspi_flash_write_security_register(QSPIFlash *dev, uint32_t addr, uint8_t val) { - QSPIFlashPart *part = dev->state->part; - status_t ret; - uint8_t out[5]; - uint8_t len; - - ret = prv_qspi_security_register_check(dev, addr); - if (ret != S_SUCCESS) { - return ret; - } - - if (part->size > ADDR_4BYTE_THRESHOLD) { - len = 5U; - out[0] = (addr >> 24U); - out[1] = (addr >> 16U) & 0xFFU; - out[2] = (addr >> 8U) & 0xFFU; - out[3] = addr & 0xFFU; - out[4] = val; - } else { - len = 4U; - out[0] = (addr >> 16U) & 0xFFU; - out[1] = (addr >> 8U) & 0xFFU; - out[2] = addr & 0xFFU; - out[3] = val; - } - - prv_write_enable(dev); - - prv_cinstr_write(dev, part->instructions.program_sec, out, len); - - while (prv_wip_check(dev)) { - } - - return 0; -} - -const FlashSecurityRegisters *qspi_flash_security_registers_info(QSPIFlash *dev) { - QSPIFlashPart *part = dev->state->part; - - return &part->sec_registers; -} - -#ifdef CONFIG_RECOVERY_FW -status_t qspi_flash_lock_security_register(QSPIFlash *dev, uint32_t addr) { - uint8_t sr[2]; - status_t res; - - res = prv_qspi_security_register_check(dev, addr); - if (res != S_SUCCESS) { - return res; - } - - prv_read_sr1(dev, &sr[0]); - prv_read_sr2(dev, &sr[1]); - - sr[1] |= (1U << SEC_ADDR_TO_IDX(addr)) << 3U; - - prv_write_sr(dev, sr, 2U); - - return 0; -} -#endif // CONFIG_RECOVERY_FW - -status_t qspi_flash_write_protection_enable(QSPIFlash *dev) { - return S_NO_ACTION_REQUIRED; -} - -status_t qspi_flash_lock_sector(QSPIFlash *dev, uint32_t addr) { - return S_SUCCESS; -} - -status_t qspi_flash_unlock_all(QSPIFlash *dev) { - return S_SUCCESS; -} \ No newline at end of file diff --git a/src/fw/drivers/otp/otp_flash.c b/src/fw/drivers/otp/otp_flash.c index 27e37c227b..32bb08a5de 100644 --- a/src/fw/drivers/otp/otp_flash.c +++ b/src/fw/drivers/otp/otp_flash.c @@ -3,41 +3,32 @@ #include -#include #include +#include #define FLASH_ERASE_VAL 0xFFU #define OTP_SLOT_SIZE 32U #define SEC_REG_IDX 0x0U -bool cd_flash_active(void); -status_t cd_flash_read_security_register(uint32_t addr, uint8_t *val); -status_t cd_flash_security_register_is_locked(uint32_t addr, bool *locked); - static char s_slot[NUM_OTP_SLOTS][OTP_SLOT_SIZE]; -char * otp_get_slot(const uint8_t index) { - const FlashSecurityRegisters *info; - status_t ret; +static const struct pbl_flash_sec_regs *prv_sec_regs(void) { + const struct pbl_flash_sec_regs *regs = FLASH->sec_regs; - if (index >= NUM_OTP_SLOTS) { - return NULL; - } + return (regs != NULL && regs->count > 0U) ? regs : NULL; +} - info = flash_security_registers_info(); - if (info->num_sec_regs == 0U) { +char *otp_get_slot(const uint8_t index) { + const struct pbl_flash_sec_regs *regs = prv_sec_regs(); + + if (index >= NUM_OTP_SLOTS || regs == NULL) { return NULL; } for (uint8_t i = 0U; i < OTP_SLOT_SIZE; i++) { - if (cd_flash_active()) { - ret = cd_flash_read_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, - (uint8_t *)&s_slot[index][i]); - } else { - ret = flash_read_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, - (uint8_t *)&s_slot[index][i]); - } - if (ret != S_SUCCESS) { + int ret = pbl_flash_sec_reg_read(FLASH, regs->addrs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, + (uint8_t *)&s_slot[index][i]); + if (ret != 0) { return NULL; } } @@ -45,26 +36,19 @@ char * otp_get_slot(const uint8_t index) { return s_slot[index]; } -uint8_t * otp_get_lock(const uint8_t index) { +uint8_t *otp_get_lock(const uint8_t index) { return NULL; } bool otp_is_locked(const uint8_t index) { - const FlashSecurityRegisters *info; - status_t ret; + const struct pbl_flash_sec_regs *regs = prv_sec_regs(); bool locked; - info = flash_security_registers_info(); - if (info->num_sec_regs == 0U) { + if (regs == NULL) { return false; } - if (cd_flash_active()) { - ret = cd_flash_security_register_is_locked(info->sec_regs[SEC_REG_IDX], &locked); - } else { - ret = flash_security_register_is_locked(info->sec_regs[SEC_REG_IDX], &locked); - } - if (ret != S_SUCCESS) { + if (pbl_flash_sec_reg_is_locked(FLASH, regs->addrs[SEC_REG_IDX], &locked) != 0) { return false; } @@ -72,12 +56,11 @@ bool otp_is_locked(const uint8_t index) { } OtpWriteResult otp_write_slot(const uint8_t index, const char *value) { - const FlashSecurityRegisters *info; + const struct pbl_flash_sec_regs *regs = prv_sec_regs(); char *existing_val; - status_t ret; size_t len; - if (index >= NUM_OTP_SLOTS) { + if (index >= NUM_OTP_SLOTS || regs == NULL) { return OtpWriteFailCorrupt; } @@ -93,15 +76,10 @@ OtpWriteResult otp_write_slot(const uint8_t index, const char *value) { } } - info = flash_security_registers_info(); - if (info->num_sec_regs == 0U) { - return OtpWriteFailCorrupt; - } - for (size_t i = 0U; i <= len; i++) { - ret = flash_write_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, - (uint8_t)value[i]); - if (ret != S_SUCCESS) { + int ret = pbl_flash_sec_reg_write(FLASH, regs->addrs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, + (uint8_t)value[i]); + if (ret != 0) { return OtpWriteFailCorrupt; } } diff --git a/src/fw/drivers/qemu/qemu_flash_hal.c b/src/fw/drivers/qemu/qemu_flash_hal.c deleted file mode 100644 index 8152eab622..0000000000 --- a/src/fw/drivers/qemu/qemu_flash_hal.c +++ /dev/null @@ -1,265 +0,0 @@ -/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include -#include - -#include "board/board.h" -#include "system/passert.h" -#include "system/status_codes.h" - -#include - -#define REG32(addr) (*(volatile uint32_t *)(addr)) - -// External flash controller MMIO register offsets -#define FLASH_CMD 0x00 -#define FLASH_ADDR 0x04 -#define FLASH_STATUS 0x08 -#define FLASH_INT_CTRL 0x0C -#define FLASH_INT_STATUS 0x10 -#define FLASH_SIZE 0x14 -// Range-based persistence handshake with the QEMU pebble-extflash device. -// The device used to auto-flush its in-RAM XIP storage on SIGTERM, but that -// captured torn writes (flash_logging journal mid-update, PFS OVERWRITE_STARTED -// with no matching COMPLETE, etc.) and wedged the next boot. Now we drive -// persistence ourselves after each individual page write: -// 1. write FLASH_SYNC_LEN = bytes_written -// 2. write FLASH_SYNC = XIP address of the start of that range -// QEMU then blk_pwrite()s just that range to the backing file. Erases are -// auto-flushed in QEMU (the geometry is implicit in FLASH_ADDR + CMD), so we -// don't need to SYNC them from here. -#define FLASH_SYNC_LEN 0x18 -#define FLASH_SYNC 0x1C - -// CMD values -#define CMD_ERASE_SUBSECTOR 1 -#define CMD_ERASE_SECTOR 2 -#define CMD_WRITE_ENABLE 3 - -// STATUS bits -#define STATUS_BUSY (1 << 0) -#define STATUS_COMPLETE (1 << 1) - -// Standard flash geometry -#define QEMU_SECTOR_SIZE 0x10000 // 64 KB -#define QEMU_SUBSECTOR_SIZE 0x1000 // 4 KB -#define QEMU_PAGE_SIZE 256 - -static bool s_initialized; - -status_t flash_impl_init(bool coredump_mode) { - (void)coredump_mode; - s_initialized = true; - return S_SUCCESS; -} - -status_t flash_impl_set_burst_mode(bool enable) { - (void)enable; - return S_SUCCESS; -} - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - return addr & ~(QEMU_SECTOR_SIZE - 1); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return addr & ~(QEMU_SUBSECTOR_SIZE - 1); -} - -size_t flash_impl_get_capacity(void) { - return REG32(QEMU_EXTFLASH_BASE + FLASH_SIZE); -} - -status_t flash_impl_enter_low_power_mode(void) { - return S_SUCCESS; -} - -status_t flash_impl_exit_low_power_mode(void) { - return S_SUCCESS; -} - -status_t flash_impl_read_sync(void *buffer, FlashAddress addr, size_t len) { - // Flash addresses already include the XIP base (0x10000000), read directly - const uint8_t *src = (const uint8_t *)(uintptr_t)addr; - memcpy(buffer, src, len); - return S_SUCCESS; -} - -status_t flash_impl_read_dma_begin(void *buffer, FlashAddress addr, size_t len) { - // No DMA in QEMU; do a synchronous read and report completion - status_t result = flash_impl_read_sync(buffer, addr, len); - flash_impl_on_read_dma_complete_from_isr(result); - return result; -} - -void flash_impl_enable_write_protection(void) { - // No-op for QEMU -} - -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { - (void)start_sector; - (void)end_sector; - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { - return S_SUCCESS; -} - -static size_t s_last_write_len; - -int flash_impl_write_page_begin(const void *buffer, FlashAddress addr, size_t len) { - // Calculate how many bytes we can write in this page - size_t page_offset = addr & (QEMU_PAGE_SIZE - 1); - size_t page_remaining = QEMU_PAGE_SIZE - page_offset; - size_t write_len = (len < page_remaining) ? len : page_remaining; - - // Write directly to XIP region - addr already includes base - volatile uint8_t *dst = (volatile uint8_t *)(uintptr_t)addr; - const uint8_t *src = (const uint8_t *)buffer; - for (size_t i = 0; i < write_len; i++) { - dst[i] = src[i]; - } - - REG32(QEMU_EXTFLASH_BASE + FLASH_SYNC_LEN) = (uint32_t)write_len; - REG32(QEMU_EXTFLASH_BASE + FLASH_SYNC) = (uint32_t)addr; - s_last_write_len = write_len; - return (int)write_len; -} - -status_t flash_impl_get_write_status(void) { - // Writes complete immediately in QEMU - return S_SUCCESS; -} - -status_t flash_impl_write_suspend(FlashAddress addr) { - (void)addr; - return S_SUCCESS; -} - -status_t flash_impl_write_resume(FlashAddress addr) { - (void)addr; - return S_SUCCESS; -} - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - REG32(QEMU_EXTFLASH_BASE + FLASH_ADDR) = subsector_addr; - REG32(QEMU_EXTFLASH_BASE + FLASH_CMD) = CMD_ERASE_SUBSECTOR; - return S_SUCCESS; -} - -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - REG32(QEMU_EXTFLASH_BASE + FLASH_ADDR) = sector_addr; - REG32(QEMU_EXTFLASH_BASE + FLASH_CMD) = CMD_ERASE_SECTOR; - return S_SUCCESS; -} - -status_t flash_impl_erase_bulk_begin(void) { - // Erase the entire flash by erasing sector by sector - size_t capacity = flash_impl_get_capacity(); - for (size_t addr = 0; addr < capacity; addr += QEMU_SECTOR_SIZE) { - flash_impl_erase_sector_begin(addr); - // Wait for completion - while (flash_impl_get_erase_status() == E_BUSY) { - // spin - } - } - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { - uint32_t status = REG32(QEMU_EXTFLASH_BASE + FLASH_STATUS); - if (status & STATUS_BUSY) { - return E_BUSY; - } - return S_SUCCESS; -} - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { - return 1; // QEMU erases are instantaneous -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { - return 1; // QEMU erases are instantaneous -} - -status_t flash_impl_erase_suspend(FlashAddress addr) { - (void)addr; - // Check if erase already completed - if (flash_impl_get_erase_status() == S_SUCCESS) { - return S_NO_ACTION_REQUIRED; - } - return S_SUCCESS; -} - -status_t flash_impl_erase_resume(FlashAddress addr) { - (void)addr; - return S_SUCCESS; -} - -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - FlashAddress base = flash_impl_get_subsector_base_address(addr); - const uint8_t *p = (const uint8_t *)(uintptr_t)base; - for (size_t i = 0; i < QEMU_SUBSECTOR_SIZE; i++) { - if (p[i] != 0xFF) { - return S_FALSE; - } - } - return S_TRUE; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - FlashAddress base = flash_impl_get_sector_base_address(addr); - const uint8_t *p = (const uint8_t *)(uintptr_t)base; - for (size_t i = 0; i < QEMU_SECTOR_SIZE; i++) { - if (p[i] != 0xFF) { - return S_FALSE; - } - } - return S_TRUE; -} - -void flash_impl_use(void) { - // No power management needed for QEMU flash -} - -void flash_impl_release(void) { -} - -void flash_impl_release_many(uint32_t num_locks) { - (void)num_locks; -} - -status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { - (void)addr; - *val = 0xFF; - return S_SUCCESS; -} - -status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { - (void)address; - *locked = false; - return S_SUCCESS; -} - -status_t flash_impl_erase_security_register(uint32_t addr) { - (void)addr; - return S_SUCCESS; -} - -status_t flash_impl_write_security_register(uint32_t addr, uint8_t val) { - (void)addr; - (void)val; - return S_SUCCESS; -} - -static const FlashSecurityRegisters s_security_regs = { - .sec_regs = NULL, - .num_sec_regs = 0, - .sec_reg_size = 0, -}; - -const FlashSecurityRegisters *flash_impl_security_registers_info(void) { - return &s_security_regs; -} diff --git a/src/fw/drivers/rtc/sf32lb.c b/src/fw/drivers/rtc/sf32lb.c index b6631b18fc..d6b522d19c 100644 --- a/src/fw/drivers/rtc/sf32lb.c +++ b/src/fw/drivers/rtc/sf32lb.c @@ -7,6 +7,7 @@ #include "board/board.h" #include +#include #include #include "flash_region/flash_region.h" #include "kernel/events.h" @@ -414,14 +415,15 @@ void rtc_set_timezone(TimezoneInfo* tzinfo) { }; memcpy(storage.tm_zone, tzinfo->tm_zone, TZ_LEN - 1); - flash_erase_subsector_blocking(FLASH_REGION_TZINFO_BEGIN); - flash_write_bytes((const uint8_t*)&storage, FLASH_REGION_TZINFO_BEGIN, sizeof(TzinfoFlashStorage)); + pbl_flash_erase(FLASH, FLASH_REGION_TZINFO_BEGIN, SUBSECTOR_SIZE_BYTES); + pbl_flash_write(FLASH, FLASH_REGION_TZINFO_BEGIN, (const uint8_t*)&storage, + sizeof(TzinfoFlashStorage)); } void rtc_get_timezone(TimezoneInfo* tzinfo) { TzinfoFlashStorage storage; - flash_read_bytes((uint8_t*)&storage, FLASH_REGION_TZINFO_BEGIN, sizeof(TzinfoFlashStorage)); + pbl_flash_read(FLASH, FLASH_REGION_TZINFO_BEGIN, (uint8_t*)&storage, sizeof(TzinfoFlashStorage)); if (storage.version != TZINFO_VERSION) { // Future versions can handle migrations here @@ -439,7 +441,7 @@ void rtc_get_timezone(TimezoneInfo* tzinfo) { } void rtc_timezone_clear(void) { - flash_erase_subsector_blocking(FLASH_REGION_TZINFO_BEGIN); + pbl_flash_erase(FLASH, FLASH_REGION_TZINFO_BEGIN, SUBSECTOR_SIZE_BYTES); } uint16_t rtc_get_timezone_id(void) { @@ -453,7 +455,7 @@ uint16_t rtc_get_timezone_id(void) { bool rtc_is_timezone_set(void) { uint8_t version; - flash_read_bytes((uint8_t*)&version, FLASH_REGION_TZINFO_BEGIN, sizeof(version)); + pbl_flash_read(FLASH, FLASH_REGION_TZINFO_BEGIN, (uint8_t*)&version, sizeof(version)); return version == TZINFO_VERSION; } diff --git a/src/fw/drivers/sf32lb52/qspi.c b/src/fw/drivers/sf32lb52/qspi.c deleted file mode 100644 index a2f66dcb13..0000000000 --- a/src/fw/drivers/sf32lb52/qspi.c +++ /dev/null @@ -1,401 +0,0 @@ -/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include "pbl/kernel/irq.h" -#include "board/board.h" -#include -#include -#include -#include "flash_region/flash_region.h" -#include "kernel/pbl_malloc.h" -#include "pbl/mcu/cache.h" -#include "system/passert.h" -#include "system/status_codes.h" - -#define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) - -static bool prv_blank_check_poll(uint32_t addr, bool is_subsector) { - const uint32_t size_bytes = is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES; - const uint32_t BUF_SIZE_BYTES = 128; - const uint32_t BUF_SIZE_WORDS = BUF_SIZE_BYTES / sizeof(uint32_t); - const uint32_t FLASH_RESET_WORD_VALUE = 0xFFFFFFFF; - uint32_t buffer[BUF_SIZE_WORDS]; - for (uint32_t offset = 0; offset < size_bytes; offset += BUF_SIZE_BYTES) { - flash_impl_read_sync(buffer, addr + offset, BUF_SIZE_BYTES); - for (uint32_t i = 0; i < BUF_SIZE_WORDS; ++i) { - if (buffer[i] != FLASH_RESET_WORD_VALUE) { - return false; - } - } - } - return true; -} - -static int prv_erase_nor(QSPIFlash *dev, uint32_t addr, uint32_t size) { - FLASH_HandleTypeDef *hflash; - uint32_t taddr, remain; - int res; - - hflash = &dev->qspi->state->ctx.handle; - - if ((addr < hflash->base) || (addr > (hflash->base + hflash->size))) { - return -1; - } - - taddr = addr - hflash->base; - remain = size; - - if ((taddr & (SUBSECTOR_SIZE_BYTES - 1)) != 0) { - return -1; - } - - if ((remain & (SUBSECTOR_SIZE_BYTES - 1)) != 0) { - return -1; - } - - while (remain > 0) { - pbl_irq_lock(); - if ((taddr & (SECTOR_SIZE_BYTES - 1)) == 0 && remain >= SECTOR_SIZE_BYTES) { - res = HAL_QSPIEX_BLK64_ERASE(hflash, taddr); - pbl_irq_unlock(); - if (res != 0) { - res = -1; - goto end; - } - remain -= SECTOR_SIZE_BYTES; - taddr += SECTOR_SIZE_BYTES; - } else { - res = HAL_QSPIEX_SECT_ERASE(hflash, taddr); - pbl_irq_unlock(); - if (res != 0) { - res = -1; - goto end; - } - remain -= SUBSECTOR_SIZE_BYTES; - taddr += SUBSECTOR_SIZE_BYTES; - } - } - -end: - SCB_InvalidateDCache_by_Addr((void *)addr, size); - SCB_InvalidateICache_by_Addr((void *)addr, size); - - return res; -} - -static int prv_write_nor(QSPIFlash *dev, uint32_t addr, uint8_t *buf, uint32_t size) { - FLASH_HandleTypeDef *hflash; - uint32_t taddr, start, remain, fill; - uint8_t *tbuf; - int res; - uint8_t *local_buf = NULL; - - hflash = &dev->qspi->state->ctx.handle; - - if ((addr < hflash->base) || (addr > (hflash->base + hflash->size))) { - return -1; - } - - if (IS_SAME_FLASH_ADDR(buf, addr) || IS_SPI_NONDMA_RAM_ADDR(buf) || - (IS_DMA_ACCROSS_1M_BOUNDARY((uint32_t)buf, size))) { - local_buf = kernel_malloc_check(size); - memcpy(local_buf, buf, size); - tbuf = local_buf; - } else { - tbuf = buf; - } - - // The QSPI write path reads `tbuf` over DMA, which bypasses the D-cache. - // Push any CPU-dirtied lines back to SRAM first so the flash receives the - // freshly-prepared bytes rather than stale memory. - uintptr_t flush_addr = (uintptr_t)tbuf; - size_t flush_size = size; - dcache_align(&flush_addr, &flush_size); - dcache_flush((const void *)flush_addr, flush_size); - - taddr = addr - hflash->base; - remain = size; - - start = taddr & (PAGE_SIZE_BYTES - 1); - // start address not page aligned - if (start > 0) { - fill = PAGE_SIZE_BYTES - start; - if (fill > size) { - fill = size; - } - - pbl_irq_lock(); - res = HAL_QSPIEX_WRITE_PAGE(hflash, taddr, tbuf, fill); - pbl_irq_unlock(); - if ((uint32_t)res != fill) { - res = -1; - goto end; - } - - taddr += fill; - tbuf += fill; - remain -= fill; - } - - while (remain > 0) { - fill = remain > PAGE_SIZE_BYTES ? PAGE_SIZE_BYTES : remain; - pbl_irq_lock(); - res = HAL_QSPIEX_WRITE_PAGE(hflash, taddr, tbuf, fill); - pbl_irq_unlock(); - if ((uint32_t)res != fill) { - res = -1; - goto end; - } - - taddr += fill; - tbuf += fill; - remain -= fill; - } - - res = size; - -end: - SCB_InvalidateDCache_by_Addr((void *)addr, size); - SCB_InvalidateICache_by_Addr((void *)addr, size); - - if (local_buf) { - kernel_free(local_buf); - } - - return res; -} - -bool qspi_flash_check_whoami(QSPIFlash *dev) { - QSPI_FLASH_CTX_T *ctx = &dev->qspi->state->ctx; - uint32_t id = ctx->dev_id; - - if (id == dev->state->part->qspi_id_value) { - PBL_LOG_DBG("Flash is %s", dev->state->part->name); - return true; - } else { - PBL_LOG_ERR("Flash isn't expected %s (whoami: 0x%" PRIx32 ")", - dev->state->part->name, id); - return false; - } -} - -status_t qspi_flash_write_protection_enable(QSPIFlash *dev) { return S_NO_ACTION_REQUIRED; } - -status_t qspi_flash_lock_sector(QSPIFlash *dev, uint32_t addr) { return S_SUCCESS; } - -status_t qspi_flash_unlock_all(QSPIFlash *dev) { return S_SUCCESS; } - -void qspi_flash_init(QSPIFlash *dev, QSPIFlashPart *part, bool coredump_mode) { - HAL_StatusTypeDef res; - - if (dev->qspi->state->initialized) { - if (coredump_mode) { - dev->qspi->state->ctx.handle.dma = NULL; - } else { - dev->qspi->state->ctx.handle.dma = &dev->qspi->state->hdma; - } - - return; - } - - dev->state->part = part; - dev->qspi->state->ctx.dual_mode = 1; - - res = HAL_FLASH_Init(&dev->qspi->state->ctx, &dev->qspi->state->cfg, - &dev->qspi->state->hdma, &dev->qspi->state->dma, - dev->qspi->clk_div); - - PBL_ASSERT(res == HAL_OK, "HAL_FLASH_Init failed"); - - qspi_flash_check_whoami(dev); - - dev->qspi->state->initialized = true; -} - -status_t qspi_flash_is_erase_complete(QSPIFlash *dev) { - // A call to the HAL_QSPIEX_SECT_ERASE interface will always return success after the call - return S_SUCCESS; -} - -status_t qspi_flash_erase_begin(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - if (prv_erase_nor(dev, addr, is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES) != 0) { - return E_ERROR; - } - - return S_SUCCESS; -} - -status_t qspi_flash_erase_suspend(QSPIFlash *dev, uint32_t addr) { - // Everything will be blocked during the erase process, so nothing will happen if you call this - // function. - return S_SUCCESS; -} - -void qspi_flash_erase_resume(QSPIFlash *dev, uint32_t addr) { - // Everything will be blocked during the erase process, so nothing will happen if you call this - // function. -} - -void qspi_flash_read_blocking(QSPIFlash *dev, uint32_t addr, void *buffer, uint32_t length) { - PBL_ASSERT(length > 0, "flash_impl_read_sync() called with 0 bytes to read"); - memcpy(buffer, (void *)(addr), length); -} - -int qspi_flash_write_page_begin(QSPIFlash *dev, const void *buffer, uint32_t addr, - uint32_t length) { - return prv_write_nor(dev, addr, (uint8_t *)buffer, length); -} - -status_t qspi_flash_get_write_status(QSPIFlash *dev) { - // It will be done in HAL_QSPIEX_WRITE_PAGE, so it must return success - return S_SUCCESS; -} - -void qspi_flash_set_lower_power_mode(QSPIFlash *dev, bool active) { - if (active) { - HAL_FLASH_NOP_CMD(&dev->qspi->state->ctx.handle); - } -} - -status_t qspi_flash_blank_check(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - return prv_blank_check_poll(addr, is_subsector); -} - -status_t prv_qspi_security_register_check(QSPIFlash *dev, uint32_t addr) { - bool addr_valid = false; - - if (dev->state->part->sec_registers.num_sec_regs == 0U) { - return E_INVALID_OPERATION; - } - - for (uint8_t i = 0U; i < dev->state->part->sec_registers.num_sec_regs; ++i) { - if (addr >= dev->state->part->sec_registers.sec_regs[i] && - addr < dev->state->part->sec_registers.sec_regs[i] + - dev->state->part->sec_registers.sec_reg_size) { - addr_valid = true; - break; - } - } - - if (!addr_valid) { - return E_INVALID_ARGUMENT; - } - - return S_SUCCESS; -} - -status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_t *val) { - FLASH_HandleTypeDef *hflash = &dev->qspi->state->ctx.handle; - int res; - - res = prv_qspi_security_register_check(dev, addr); - if (res != S_SUCCESS) { - return res; - } - - /* Security register read size should aligned with 4 bytes. */ - uint8_t values[4] = {0}; - uint32_t offset = addr % 4; - uint32_t base_addr = addr - offset; - - pbl_irq_lock(); - res = HAL_QSPI_READ_OTP(hflash, base_addr, values, 4); - pbl_irq_unlock(); - - if (res != 4) { - return E_ERROR; - } - - *val = values[offset]; - - return S_SUCCESS; -} - -status_t qspi_flash_security_register_is_locked(QSPIFlash *dev, uint32_t addr, bool *locked) { - FLASH_HandleTypeDef *hflash = &dev->qspi->state->ctx.handle; - uint8_t opt_val = 0; - - /* OPT operation are synchronous, one match means all matched. */ - pbl_irq_lock(); - opt_val = HAL_QSPI_GET_OTP_LB(hflash); - pbl_irq_unlock(); - - if (opt_val == 0xff) { - return E_ERROR; - } - - /* Security registers address*/ - if ((opt_val & (1U << SEC_ADDR_TO_IDX(addr))) != 0U) { - *locked = true; - } else { - *locked = false; - } - - return S_SUCCESS; -} - -status_t qspi_flash_erase_security_register(QSPIFlash *dev, uint32_t addr) { - FLASH_HandleTypeDef *hflash = &dev->qspi->state->ctx.handle; - int res; - - res = prv_qspi_security_register_check(dev, addr); - if (res != S_SUCCESS) { - return res; - } - - pbl_irq_lock(); - res = HAL_QSPI_ERASE_OTP(hflash, addr); - pbl_irq_unlock(); - - if (res != 0) { - return E_ERROR; - } - - return S_SUCCESS; -} - -status_t qspi_flash_write_security_register(QSPIFlash *dev, uint32_t addr, uint8_t val) { - FLASH_HandleTypeDef *hflash = &dev->qspi->state->ctx.handle; - int res; - - res = prv_qspi_security_register_check(dev, addr); - if (res != S_SUCCESS) { - return res; - } - - uintptr_t flush_addr = (uintptr_t)&val; - size_t flush_size = sizeof(val); - dcache_align(&flush_addr, &flush_size); - dcache_flush((const void *)flush_addr, flush_size); - - pbl_irq_lock(); - res = HAL_QSPI_WRITE_OTP(hflash, addr, &val, 1); - pbl_irq_unlock(); - - if (res != 1) { - return E_ERROR; - } - - return S_SUCCESS; -} - -const FlashSecurityRegisters *qspi_flash_security_registers_info(QSPIFlash *dev) { - return &dev->state->part->sec_registers; -} - -#ifdef CONFIG_RECOVERY_FW -status_t qspi_flash_lock_security_register(QSPIFlash *dev, uint32_t addr) { - FLASH_HandleTypeDef *hflash = &dev->qspi->state->ctx.handle; - int res; - - pbl_irq_lock(); - res = HAL_QSPI_LOCK_OTP(hflash, addr); - pbl_irq_unlock(); - - if (res != 0) { - return E_ERROR; - } - - return S_SUCCESS; -} -#endif // CONFIG_RECOVERY_FW diff --git a/src/fw/flash_region/filesystem_regions.c b/src/fw/flash_region/filesystem_regions.c index ef1a198b69..3f63edc788 100644 --- a/src/fw/flash_region/filesystem_regions.c +++ b/src/fw/flash_region/filesystem_regions.c @@ -2,12 +2,13 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "flash_region/filesystem_regions.h" +#include #include "pbl/util/size.h" void filesystem_regions_erase_all(void) { for (unsigned int i = 0; i < ARRAY_LENGTH(s_region_list); i++) { - flash_region_erase_optimal_range_no_watchdog(s_region_list[i].start, s_region_list[i].start, - s_region_list[i].end, s_region_list[i].end); + pbl_flash_erase(FLASH, s_region_list[i].start, + s_region_list[i].end - s_region_list[i].start); } } diff --git a/src/fw/flash_region/flash_region.c b/src/fw/flash_region/flash_region.c deleted file mode 100644 index bf6b16f777..0000000000 --- a/src/fw/flash_region/flash_region.c +++ /dev/null @@ -1,104 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include "flash_region.h" - -#include -#include -#include "kernel/util/sleep.h" -#include -#include "system/passert.h" - -#include - -//! Do some upkeep in between erases to keep the rest of the system stable since erases block -//! the current task for so long. -//! -//! @param[in, out] erase_count Counter variable to track how many times we've run this upkeep -//! function -//! @param feed_watchdog Whether we should feed the task_watchdog for the current task or not -static void prv_erase_upkeep(int *erase_count, bool feed_watchdog) { - (*erase_count)++; - if ((*erase_count %= 2) == 0) { - // Sleep if this is the second time in a row calling erase (sub)sector. - - // FIXME: We could check and see if we are starving tasks and only force a - // context switch for that case - psleep((SECTOR_SIZE_BYTES > (64 * 1024)) ? 20 : 4); - } - - if (feed_watchdog) { - task_watchdog_bit_set(pebble_task_get_current()); - } -} - -static void prv_erase_optimal_range(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end, bool feed_watchdog) { - PBL_LOG_DBG("flash_region_erase_optimal_range, 0x%"PRIx32" 0x%"PRIx32" 0x%"PRIx32" 0x%"PRIx32, - min_start, max_start, min_end, max_end); - - PBL_ASSERTN(((min_start & (~SUBSECTOR_ADDR_MASK)) == 0) && - ((max_end & (~SUBSECTOR_ADDR_MASK)) == 0) && - (min_start <= max_start) && - (max_start <= min_end) && - (min_end <= max_end)); - - // We want to erase the sector that starts immediately below max_start but after min_start. If no sector - // boundary exists between the two, we need to start erasing sectors after min_start and backfill with - // subsector erases. - int32_t sector_start = (max_start & SECTOR_ADDR_MASK); - int32_t subsector_start = (max_start & SUBSECTOR_ADDR_MASK); - if (sector_start < (int32_t) min_start) { - sector_start += SECTOR_SIZE_BYTES; - } - - // We want to erase ending after min_end but before max_end. If that ends running past the end of max_end, - // we need to erase starting with the sector before and fill in with subsector erases. - int32_t sector_end = ((min_end - 1) & SECTOR_ADDR_MASK) + SECTOR_SIZE_BYTES; - int32_t subsector_end = ((min_end - 1) & SUBSECTOR_ADDR_MASK) + SUBSECTOR_SIZE_BYTES; - if (sector_end > (int32_t) max_end) { - sector_end -= SECTOR_SIZE_BYTES; - } - - int erase_count = 0; - - // Do the upkeep immediately just in case we've spent awhile running without feeding the - // watchdog before doing this erase operation. - prv_erase_upkeep(&erase_count, feed_watchdog); - - if (sector_start < sector_end) { - // Now erase the leading subsectors... - for (int32_t i = subsector_start; i < sector_start; i += SUBSECTOR_SIZE_BYTES) { - flash_erase_subsector_blocking(i); - prv_erase_upkeep(&erase_count, feed_watchdog); - } - - // Erase the full sectors... - for (int32_t i = sector_start; i < sector_end; i += SECTOR_SIZE_BYTES) { - flash_erase_sector_blocking(i); - prv_erase_upkeep(&erase_count, feed_watchdog); - } - - // Erase the trailing subsectors - for (int32_t i = sector_end; i < subsector_end; i += SUBSECTOR_SIZE_BYTES) { - flash_erase_subsector_blocking(i); - prv_erase_upkeep(&erase_count, feed_watchdog); - } - } else { - // Can't erase any full sectors, just erase subsectors the whole way. - for (int32_t i = subsector_start; i < subsector_end; i += SUBSECTOR_SIZE_BYTES) { - flash_erase_subsector_blocking(i); - prv_erase_upkeep(&erase_count, feed_watchdog); - } - } -} - -void flash_region_erase_optimal_range(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end) { - prv_erase_optimal_range(min_start, max_start, min_end, max_end, false /* feed_watchdog */); -} - -void flash_region_erase_optimal_range_no_watchdog(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end) { - prv_erase_optimal_range(min_start, max_start, min_end, max_end, true /* feed_watchdog */); -} diff --git a/src/fw/flash_region/flash_region.h b/src/fw/flash_region/flash_region.h index 96537504be..1e769d2ae5 100644 --- a/src/fw/flash_region/flash_region.h +++ b/src/fw/flash_region/flash_region.h @@ -42,17 +42,3 @@ #define FLASH_REGION_FIRMWARE_DEST_START (FLASH_REGION_FIRMWARE_DEST_BEGIN + FIRMWARE_OFFSET) #define FLASH_REGION_SAFE_FIRMWARE_START (FLASH_REGION_SAFE_FIRMWARE_BEGIN + FIRMWARE_OFFSET) - -// NOTE: The following functions are deprecated! New code should use the -// asynchronous version, flash_erase_optimal_range, in flash.h. - -//! Erase at least (max_start, min_end) but no more than (min_start, max_end) using as few erase -//! operations as possible. (min_start, max_end) must be both 4kb aligned, as that's the smallest -//! unit that we can erase. -void flash_region_erase_optimal_range(uint32_t min_start, uint32_t max_start, uint32_t min_end, - uint32_t max_end); - -//! The same as flash_region_erase_optimal_range but first disables the task watchdog for the -//! current task. -void flash_region_erase_optimal_range_no_watchdog(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end); diff --git a/src/fw/kernel/core_dump.c b/src/fw/kernel/core_dump.c index 0da4471b2b..ec4664e6f2 100644 --- a/src/fw/kernel/core_dump.c +++ b/src/fw/kernel/core_dump.c @@ -27,6 +27,7 @@ extern char *itoa(int value, char *str, int base); #include "logging/pulse_logging.h" #include +#include "system/status_codes.h" #include #include #include @@ -70,11 +71,6 @@ extern const ElfExternalNote TINTIN_BUILD_ID; extern const uint32_t __CCM_RAM_size__[]; extern const uint32_t __DTCM_RAM_size__[]; -void cd_flash_init(void); -uint32_t cd_flash_write_bytes(const void* buffer_ptr, uint32_t start_addr, uint32_t buffer_size); -void cd_flash_erase_region(uint32_t start_addr, uint32_t total_bytes); -void cd_flash_read_bytes(void* buffer_ptr, uint32_t start_addr, uint32_t buffer_size); - // ---------------------------------------------------------------------------------------- // Private globals @@ -117,35 +113,20 @@ static const MemoryRegion LCPU_MEMORY_REGION = { }; #endif -// ------------------------------------------------------------------------------------------------- -// Flash driver dual-API. -static bool s_use_cd_flash_driver = true; - static uint32_t prv_flash_write_bytes(const void* buffer_ptr, uint32_t start_addr, uint32_t buffer_size) { - if (s_use_cd_flash_driver) { - return cd_flash_write_bytes(buffer_ptr, start_addr, buffer_size); - } else { - flash_write_bytes(buffer_ptr, start_addr, buffer_size); - return buffer_size; - } + CD_ASSERTN(((start_addr + buffer_size) <= CORE_DUMP_FLASH_END) && + (int)start_addr >= CORE_DUMP_FLASH_START); + CD_ASSERTN(pbl_flash_write(FLASH, start_addr, buffer_ptr, buffer_size) == 0); + return buffer_size; } static void prv_flash_erase_region(uint32_t start_addr, uint32_t total_bytes) { - if (s_use_cd_flash_driver) { - cd_flash_erase_region(start_addr, total_bytes); - } else { - uint32_t end = start_addr + total_bytes; - flash_region_erase_optimal_range_no_watchdog(start_addr, start_addr, end, end); - } + CD_ASSERTN(pbl_flash_erase(FLASH, start_addr, total_bytes) == 0); } static void prv_flash_read_bytes(void* buffer_ptr, uint32_t start_addr, uint32_t buffer_size) { - if (s_use_cd_flash_driver) { - cd_flash_read_bytes(buffer_ptr, start_addr, buffer_size); - } else { - flash_read_bytes(buffer_ptr, start_addr, buffer_size); - } + CD_ASSERTN(pbl_flash_read(FLASH, start_addr, buffer_ptr, buffer_size) == 0); } // ------------------------------------------------------------------------------------------------- @@ -577,9 +558,8 @@ EXTERNALLY_VISIBLE void core_dump_handler_c(void) { // Feed the watchdog so that we don't get watchdog reset in the middle of dumping the core watchdog_feed(); - // Init the flash and SPI bus - s_use_cd_flash_driver = true; - cd_flash_init(); + // Reset the flash driver into a state usable without the OS + pbl_flash_coredump_init(FLASH); // If there is a fairly recent unread core image already present, don't replace it. Once it is read through // the get_bytes_protocol_msg_callback(), the unread flag gets cleared out. @@ -701,7 +681,8 @@ status_t core_dump_size(uint32_t flash_base, uint32_t *size) { uint32_t current_offset = sizeof(CoreDumpImageHeader); while (true) { - flash_read_bytes((uint8_t *)&chunk_hdr, core_dump_base + current_offset, sizeof(chunk_hdr)); + pbl_flash_read(FLASH, core_dump_base + current_offset, (uint8_t *)&chunk_hdr, + sizeof(chunk_hdr)); if (chunk_hdr.key == CORE_DUMP_CHUNK_KEY_TERMINATOR) { current_offset += sizeof(chunk_hdr); break; @@ -726,18 +707,18 @@ status_t core_dump_size(uint32_t flash_base, uint32_t *size) { void core_dump_mark_read(uint32_t flash_base) { CoreDumpFlashRegionHeader region_hdr; - flash_read_bytes((uint8_t *)®ion_hdr, flash_base, sizeof(region_hdr)); + pbl_flash_read(FLASH, flash_base, (uint8_t *)®ion_hdr, sizeof(region_hdr)); region_hdr.unread = 0; - flash_write_bytes((uint8_t *)®ion_hdr, flash_base, sizeof(region_hdr)); + pbl_flash_write(FLASH, flash_base, (uint8_t *)®ion_hdr, sizeof(region_hdr)); } bool core_dump_is_unread_available(uint32_t flash_base) { if (flash_base != CORE_DUMP_FLASH_INVALID_ADDR) { // a coredump is on flash CoreDumpFlashRegionHeader region_hdr; CoreDumpImageHeader image_hdr; - flash_read_bytes((uint8_t *)®ion_hdr, flash_base, sizeof(region_hdr)); - flash_read_bytes((uint8_t *)&image_hdr, flash_base + sizeof(region_hdr), - sizeof(image_hdr)); + pbl_flash_read(FLASH, flash_base, (uint8_t *)®ion_hdr, sizeof(region_hdr)); + pbl_flash_read(FLASH, flash_base + sizeof(region_hdr), (uint8_t *)&image_hdr, + sizeof(image_hdr)); return ((image_hdr.magic == CORE_DUMP_MAGIC) && (region_hdr.unread != 0)); } @@ -754,9 +735,6 @@ bool core_dump_reserve_ble_slot(uint32_t *flash_base, uint32_t *max_size, bool status = true; uint32_t flash_addr, flash_addr_base; - // Use the standard flash driver - s_use_cd_flash_driver = false; - flash_addr_base = prv_flash_start_address(true /*new*/); if (flash_addr_base == CORE_DUMP_FLASH_INVALID_ADDR) { status = false; @@ -770,7 +748,6 @@ bool core_dump_reserve_ble_slot(uint32_t *flash_base, uint32_t *max_size, *max_size = CORE_DUMP_MAX_SIZE - (flash_addr - flash_addr_base); cleanup: - s_use_cd_flash_driver = true; return status; } diff --git a/src/fw/kernel/reset.c b/src/fw/kernel/reset.c index bf470a89b0..6e82f01352 100644 --- a/src/fw/kernel/reset.c +++ b/src/fw/kernel/reset.c @@ -20,7 +20,7 @@ void system_reset_prepare(void) { fw_prepare_for_reset(); - flash_stop(); + pbl_flash_stop(FLASH); } NORETURN system_reset(void) { diff --git a/src/fw/main.c b/src/fw/main.c index d2e65b1de8..968ef20d08 100644 --- a/src/fw/main.c +++ b/src/fw/main.c @@ -13,6 +13,7 @@ #include #include +#include "flash_region/flash_region.h" #include #include @@ -173,8 +174,9 @@ static void init_drivers(void) { pmic_init(); #endif - flash_init(); - flash_prf_set_protection(true); + pbl_flash_init(FLASH); + pbl_flash_protect(FLASH, FLASH_REGION_SAFE_FIRMWARE_BEGIN, + FLASH_REGION_SAFE_FIRMWARE_END - FLASH_REGION_SAFE_FIRMWARE_BEGIN); uint8_t vibe_cali = mfg_info_get_vibe_cali(); if (vibe_cali != MFG_INFO_VIBE_CALI_INVALID) { diff --git a/src/fw/mfg/asterix/mfg_info.c b/src/fw/mfg/asterix/mfg_info.c index 59e0486566..8e5a2ccb10 100644 --- a/src/fw/mfg/asterix/mfg_info.c +++ b/src/fw/mfg/asterix/mfg_info.c @@ -3,6 +3,7 @@ #include "mfg/mfg_info.h" #include +#include #include "flash_region/flash_region.h" #define CURRENT_DATA_VERSION 0 @@ -16,14 +17,14 @@ typedef struct { } MfgData; static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); + pbl_flash_erase(FLASH, FLASH_REGION_MFG_INFO_BEGIN, SUBSECTOR_SIZE_BYTES); + pbl_flash_write(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (const uint8_t*) data, sizeof(*data)); } static MfgData prv_fetch_struct(void) { MfgData result; - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); + pbl_flash_read(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (uint8_t*) &result, sizeof(result)); // Fallback data if not available if (result.data_version != CURRENT_DATA_VERSION) { diff --git a/src/fw/mfg/getafix/mfg_info.c b/src/fw/mfg/getafix/mfg_info.c index a9908aa644..aacb661f1c 100644 --- a/src/fw/mfg/getafix/mfg_info.c +++ b/src/fw/mfg/getafix/mfg_info.c @@ -3,6 +3,7 @@ #include "mfg/mfg_info.h" #include +#include #include "flash_region/flash_region.h" #define CURRENT_DATA_VERSION 0 @@ -16,14 +17,14 @@ typedef struct { } MfgData; static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); + pbl_flash_erase(FLASH, FLASH_REGION_MFG_INFO_BEGIN, SUBSECTOR_SIZE_BYTES); + pbl_flash_write(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (const uint8_t*) data, sizeof(*data)); } static MfgData prv_fetch_struct(void) { MfgData result; - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); + pbl_flash_read(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (uint8_t*) &result, sizeof(result)); // Fallback data if not available if (result.data_version != CURRENT_DATA_VERSION) { diff --git a/src/fw/mfg/obelix/mfg_info.c b/src/fw/mfg/obelix/mfg_info.c index ab94e5bc33..9993e0807d 100644 --- a/src/fw/mfg/obelix/mfg_info.c +++ b/src/fw/mfg/obelix/mfg_info.c @@ -3,6 +3,7 @@ #include "mfg/mfg_info.h" #include +#include #include "flash_region/flash_region.h" #define CURRENT_DATA_VERSION 0 @@ -16,14 +17,14 @@ typedef struct { } MfgData; static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); + pbl_flash_erase(FLASH, FLASH_REGION_MFG_INFO_BEGIN, SUBSECTOR_SIZE_BYTES); + pbl_flash_write(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (const uint8_t*) data, sizeof(*data)); } static MfgData prv_fetch_struct(void) { MfgData result; - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); + pbl_flash_read(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (uint8_t*) &result, sizeof(result)); // Fallback data if not available if (result.data_version != CURRENT_DATA_VERSION) { diff --git a/src/fw/mfg/qemu/mfg_info.c b/src/fw/mfg/qemu/mfg_info.c index c2c3a0a150..ef571c763e 100644 --- a/src/fw/mfg/qemu/mfg_info.c +++ b/src/fw/mfg/qemu/mfg_info.c @@ -3,6 +3,7 @@ #include "mfg/mfg_info.h" #include +#include #include "flash_region/flash_region.h" #define CURRENT_DATA_VERSION 0 @@ -16,14 +17,14 @@ typedef struct { } MfgData; static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); + pbl_flash_erase(FLASH, FLASH_REGION_MFG_INFO_BEGIN, SUBSECTOR_SIZE_BYTES); + pbl_flash_write(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (const uint8_t*) data, sizeof(*data)); } static MfgData prv_fetch_struct(void) { MfgData result; - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); + pbl_flash_read(FLASH, FLASH_REGION_MFG_INFO_BEGIN, (uint8_t*) &result, sizeof(result)); // Fallback data if not available if (result.data_version != CURRENT_DATA_VERSION) { diff --git a/src/fw/resource/resource_storage_flash.c b/src/fw/resource/resource_storage_flash.c index fa64fdcc83..8759e04686 100644 --- a/src/fw/resource/resource_storage_flash.c +++ b/src/fw/resource/resource_storage_flash.c @@ -88,13 +88,12 @@ static uint32_t resource_storage_system_bank_metadata_size(ResourceStoreEntry *e static uint32_t resource_storage_system_bank_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes, uint32_t entry_offset) { uint32_t start_offset = resource_store_get_metadata_size(entry) + entry_offset; - return flash_calculate_legacy_defective_checksum( - BANK.begin + start_offset, num_bytes); + return pbl_flash_legacy_checksum(FLASH, BANK.begin + start_offset, num_bytes); } static uint32_t resource_storage_system_bank_read(ResourceStoreEntry *entry, uint32_t offset, void *data, size_t num_bytes) { - flash_read_bytes(data, BANK.begin + offset, num_bytes); + pbl_flash_read(FLASH, BANK.begin + offset, data, num_bytes); return num_bytes; } @@ -127,7 +126,7 @@ static const uint8_t *resource_storage_system_bank_readonly_bytes(ResourceStoreE static void resource_storage_system_bank_clear(ResourceStoreEntry *entry) { uint8_t buffer[MANIFEST_SIZE] = {0}; - flash_write_bytes(buffer, BANK.begin, MANIFEST_SIZE); + pbl_flash_write(FLASH, BANK.begin, buffer, MANIFEST_SIZE); } diff --git a/src/fw/services/battery/nrf_fuel_gauge/battery_state.c b/src/fw/services/battery/nrf_fuel_gauge/battery_state.c index 910682d111..f66b663df9 100644 --- a/src/fw/services/battery/nrf_fuel_gauge/battery_state.c +++ b/src/fw/services/battery/nrf_fuel_gauge/battery_state.c @@ -23,6 +23,8 @@ #ifdef CONFIG_MFG #include +#include +#include "system/status_codes.h" #include "flash_region/flash_region.h" #endif @@ -95,7 +97,7 @@ static uint32_t s_save_counter; #ifdef CONFIG_MFG // In manufacturing firmware, use dedicated MFG_BATTERY_STATE flash region static void prv_erase_state(void) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_BATTERY_STATE_BEGIN); + pbl_flash_erase(FLASH, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, SUBSECTOR_SIZE_BYTES); PBL_LOG_DBG("Fuel gauge state erased"); } @@ -104,7 +106,7 @@ static bool prv_load_state(void *state, size_t size) { return false; } - flash_read_bytes(state, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, size); + pbl_flash_read(FLASH, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, state, size); // Check if the flash region contains valid data (not all 0xFF) uint8_t *bytes = (uint8_t *)state; @@ -138,8 +140,8 @@ static void prv_save_state(void) { return; } - flash_erase_subsector_blocking(FLASH_REGION_MFG_BATTERY_STATE_BEGIN); - flash_write_bytes(buf, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, sizeof(buf)); + pbl_flash_erase(FLASH, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, SUBSECTOR_SIZE_BYTES); + pbl_flash_write(FLASH, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, buf, sizeof(buf)); PBL_LOG_DBG("Fuel gauge state saved"); } diff --git a/src/fw/services/data_logging/dls_storage.c b/src/fw/services/data_logging/dls_storage.c index 43787798c7..acf225fa45 100644 --- a/src/fw/services/data_logging/dls_storage.c +++ b/src/fw/services/data_logging/dls_storage.c @@ -6,6 +6,8 @@ #include "pbl/services/data_logging/dls_list.h" #include +#include +#include "system/status_codes.h" #include "kernel/pbl_malloc.h" #include "kernel/pebble_tasks.h" #include "kernel/util/sleep.h" diff --git a/src/fw/services/filesystem/flash_translation.c b/src/fw/services/filesystem/flash_translation.c index b317a56cd4..acf2be0e3f 100644 --- a/src/fw/services/filesystem/flash_translation.c +++ b/src/fw/services/filesystem/flash_translation.c @@ -102,8 +102,7 @@ void ftl_add_region(uint32_t region_start, uint32_t region_end, bool erase_new_r // erase if asked to if (erase_new_region) { - flash_region_erase_optimal_range_no_watchdog(region_start, region_start, - region_end, region_end); + pbl_flash_erase(FLASH, region_start, region_end - region_start); } s_ftl_size += (region_end - region_start); @@ -149,19 +148,19 @@ static void prv_ftl_operation(uint8_t *buffer, uint32_t size, uint32_t offset, uint32_t bytes = MIN(curr_virt_offset_end - offset, size); if (operation == FTLRead) { - flash_read_bytes( - buffer, s_region_list[idx].start + offset - curr_virt_offset_begin, bytes); + pbl_flash_read(FLASH, s_region_list[idx].start + offset - curr_virt_offset_begin, buffer, + bytes); } else if (operation == FTLWrite ) { - flash_write_bytes( - buffer, s_region_list[idx].start + offset - curr_virt_offset_begin, bytes); + pbl_flash_write(FLASH, s_region_list[idx].start + offset - curr_virt_offset_begin, buffer, + bytes); } else if (operation == FTLEraseSubsector) { PBL_ASSERTN(size == SUBSECTOR_SIZE_BYTES); - flash_erase_subsector_blocking( - s_region_list[idx].start + offset - curr_virt_offset_begin); + pbl_flash_erase(FLASH, s_region_list[idx].start + offset - curr_virt_offset_begin, + SUBSECTOR_SIZE_BYTES); } else if (operation == FTLEraseSector) { PBL_ASSERTN(size == SECTOR_SIZE_BYTES); - flash_erase_sector_blocking( - s_region_list[idx].start + offset - curr_virt_offset_begin); + pbl_flash_erase(FLASH, s_region_list[idx].start + offset - curr_virt_offset_begin, + SECTOR_SIZE_BYTES); } size -= bytes; diff --git a/src/fw/services/filesystem/pfs.c b/src/fw/services/filesystem/pfs.c index 6ad767ed94..8f61a1fbdb 100644 --- a/src/fw/services/filesystem/pfs.c +++ b/src/fw/services/filesystem/pfs.c @@ -11,6 +11,7 @@ #include "console/prompt.h" #include +#include "system/status_codes.h" #include #include #include "flash_region/filesystem_regions.h" diff --git a/src/fw/services/get_bytes/get_bytes_storage_coredump.c b/src/fw/services/get_bytes/get_bytes_storage_coredump.c index 153de9b2ed..79193e4b56 100644 --- a/src/fw/services/get_bytes/get_bytes_storage_coredump.c +++ b/src/fw/services/get_bytes/get_bytes_storage_coredump.c @@ -32,7 +32,7 @@ static uint32_t prv_coredump_flash_base(bool unread_only) { // ---------------------------------------------------------------------------------- // First, see if the flash header has been put in place - flash_read_bytes((uint8_t *)&flash_hdr, CORE_DUMP_FLASH_START, sizeof(flash_hdr)); + pbl_flash_read(FLASH, CORE_DUMP_FLASH_START, (uint8_t *)&flash_hdr, sizeof(flash_hdr)); if (flash_hdr.magic != CORE_DUMP_FLASH_HDR_MAGIC || flash_hdr.unformatted == CORE_DUMP_ALL_UNFORMATTED) { @@ -46,7 +46,7 @@ static uint32_t prv_coredump_flash_base(bool unread_only) { } base_address = core_dump_get_slot_address(i); - flash_read_bytes((uint8_t *)®ion_hdr, base_address, sizeof(region_hdr)); + pbl_flash_read(FLASH, base_address, (uint8_t *)®ion_hdr, sizeof(region_hdr)); if (unread_only && !region_hdr.unread) { continue; @@ -80,8 +80,8 @@ GetBytesInfoErrorCode gb_storage_coredump_get_size(GetBytesStorage *storage, uin uint32_t flash_base = prv_coredump_flash_base(data->only_get_new_coredump); PBL_LOG_DBG("GET_BYTES: checking image %p", (void *)flash_base); if (flash_base != CORE_DUMP_FLASH_INVALID_ADDR) { - flash_read_bytes((uint8_t *)&image_hdr, flash_base + sizeof(CoreDumpFlashRegionHeader), - sizeof(image_hdr)); + pbl_flash_read(FLASH, flash_base + sizeof(CoreDumpFlashRegionHeader), (uint8_t *)&image_hdr, + sizeof(image_hdr)); } if (flash_base == CORE_DUMP_FLASH_INVALID_ADDR || image_hdr.magic != CORE_DUMP_MAGIC) { return GET_BYTES_DOESNT_EXIST; @@ -99,7 +99,7 @@ GetBytesInfoErrorCode gb_storage_coredump_get_size(GetBytesStorage *storage, uin bool gb_storage_coredump_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len) { GBCoredumpData *data = storage->impl_data; uint32_t image_base = data->core_dump_base + sizeof(CoreDumpFlashRegionHeader); - flash_read_bytes(buffer, image_base + storage->current_offset, len); + pbl_flash_read(FLASH, image_base + storage->current_offset, buffer, len); storage->current_offset += len; return true; } diff --git a/src/fw/services/get_bytes/get_bytes_storage_flash.c b/src/fw/services/get_bytes/get_bytes_storage_flash.c index 388bf664ed..b11afa837f 100644 --- a/src/fw/services/get_bytes/get_bytes_storage_flash.c +++ b/src/fw/services/get_bytes/get_bytes_storage_flash.c @@ -3,6 +3,7 @@ #include "pbl/services/get_bytes/get_bytes_storage.h" #include +#include #include "kernel/pbl_malloc.h" #include "flash_region/flash_region.h" @@ -25,7 +26,7 @@ GetBytesInfoErrorCode gb_storage_flash_get_size(GetBytesStorage *storage, uint32 bool gb_storage_flash_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len) { uint32_t start_offset = ((GetBytesStorageInfo *)storage->impl_data)->flash_start_addr; - flash_read_bytes(buffer, storage->current_offset + start_offset, len); + pbl_flash_read(FLASH, storage->current_offset + start_offset, buffer, len); storage->current_offset += len; return true; } diff --git a/src/fw/services/prf_update/service.c b/src/fw/services/prf_update/service.c index 486a8f6455..91e9aea3cc 100644 --- a/src/fw/services/prf_update/service.c +++ b/src/fw/services/prf_update/service.c @@ -14,7 +14,7 @@ PBL_LOG_MODULE_DEFINE(service_prf_update, CONFIG_SERVICE_PRF_UPDATE_LOG_LEVEL); #ifndef CONFIG_RECOVERY_FW static void prv_do_update(void) { PBL_LOG_INFO("Updating PRF!"); - flash_prf_set_protection(false); + pbl_flash_unprotect(FLASH); #ifndef CONFIG_PBLBOOT FirmwareDescription description = @@ -40,10 +40,7 @@ static void prv_do_update(void) { #endif PBL_LOG_DBG("Erasing previous PRF..."); - flash_region_erase_optimal_range(FLASH_REGION_SAFE_FIRMWARE_BEGIN, - FLASH_REGION_SAFE_FIRMWARE_BEGIN, - FLASH_REGION_SAFE_FIRMWARE_BEGIN + total_length, - FLASH_REGION_SAFE_FIRMWARE_END); + pbl_flash_erase(FLASH, FLASH_REGION_SAFE_FIRMWARE_BEGIN, total_length); PBL_LOG_DBG("Copying PRF from scratch to the PRF slot"); uint8_t buffer[512]; @@ -51,14 +48,15 @@ static void prv_do_update(void) { while (offset < total_length) { const uint32_t chunk_size = MIN(sizeof(buffer), (total_length - offset)); - flash_read_bytes(buffer, FLASH_REGION_FIRMWARE_DEST_BEGIN + offset, chunk_size); - flash_write_bytes(buffer, FLASH_REGION_SAFE_FIRMWARE_BEGIN + offset, chunk_size); + pbl_flash_read(FLASH, FLASH_REGION_FIRMWARE_DEST_BEGIN + offset, buffer, chunk_size); + pbl_flash_write(FLASH, FLASH_REGION_SAFE_FIRMWARE_BEGIN + offset, buffer, chunk_size); offset += chunk_size; } done: - flash_prf_set_protection(true); + pbl_flash_protect(FLASH, FLASH_REGION_SAFE_FIRMWARE_BEGIN, + FLASH_REGION_SAFE_FIRMWARE_END - FLASH_REGION_SAFE_FIRMWARE_BEGIN); PBL_LOG_DBG("Done!"); } #endif diff --git a/src/fw/services/process_management/app_storage.c b/src/fw/services/process_management/app_storage.c index a430fe30d1..1ac7bd9f44 100644 --- a/src/fw/services/process_management/app_storage.c +++ b/src/fw/services/process_management/app_storage.c @@ -9,6 +9,7 @@ #include #include +#include "system/status_codes.h" #include "process_management/pebble_process_info.h" #include "resource/resource_storage.h" #include "pbl/services/filesystem/pfs.h" diff --git a/src/fw/services/process_management/process_loader_storage.c b/src/fw/services/process_management/process_loader_storage.c index 7f0b5441df..ca859a2552 100644 --- a/src/fw/services/process_management/process_loader_storage.c +++ b/src/fw/services/process_management/process_loader_storage.c @@ -4,6 +4,7 @@ #include "process_management/process_loader.h" #include +#include "system/status_codes.h" #include "kernel/util/segment.h" #include "process_management/pebble_process_md.h" #include "pbl/services/filesystem/pfs.h" diff --git a/src/fw/services/put_bytes/put_bytes_storage_raw.c b/src/fw/services/put_bytes/put_bytes_storage_raw.c index 1259c7f7a3..8f6b38a3dc 100644 --- a/src/fw/services/put_bytes/put_bytes_storage_raw.c +++ b/src/fw/services/put_bytes/put_bytes_storage_raw.c @@ -80,7 +80,7 @@ bool pb_storage_raw_get_status(PutBytesObjectType obj_type, PbInstallStatus *st size_t bytes_to_read = MIN(bytes_left, read_buffer_size); curr_read_address -= bytes_to_read; - flash_read_bytes(read_buffer, curr_read_address, bytes_to_read); + pbl_flash_read(FLASH, curr_read_address, read_buffer, bytes_to_read); for (int i = (bytes_to_read - 1); i >= 0; i--) { if (read_buffer[i] != 0xff) { uint32_t data_at = curr_read_address + i; @@ -97,7 +97,7 @@ bool pb_storage_raw_get_status(PutBytesObjectType obj_type, PbInstallStatus *st // TODO: We are perpetuating the defective crc here. Maybe this is as good an excuse as any // for the mobile apps to implement flash_crc32 - uint32_t crc = flash_calculate_legacy_defective_checksum(stop_read_address, bytes_written); + uint32_t crc = pbl_flash_legacy_checksum(FLASH, stop_read_address, bytes_written); *status = (PbInstallStatus) { .num_bytes_written = bytes_written, @@ -139,8 +139,7 @@ bool pb_storage_raw_init(PutBytesStorage *storage, PutBytesObjectType object_typ // By erasing the entire region we make it more likely for 'pb_storage_raw_get_status' to // recover the correct location. - flash_region_erase_optimal_range(layout->start_address, layout->start_address, - layout->end_address, layout->end_address); + pbl_flash_erase(FLASH, layout->start_address, layout->end_address - layout->start_address); // Restore the fast interval so the init ACK isn't delayed by the slow connection parameters. comm_session_set_responsiveness(comm_session_get_system_session(), BtConsumerPpPutBytes, @@ -167,7 +166,7 @@ void pb_storage_raw_write(PutBytesStorage *storage, uint32_t offset, const uint8 const MemoryLayout *layout = storage->impl_data; const uint32_t flash_address = layout->start_address + offset; - flash_write_bytes(buffer, flash_address, length); + pbl_flash_write(FLASH, flash_address, buffer, length); } uint32_t pb_storage_raw_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type) { @@ -176,10 +175,10 @@ uint32_t pb_storage_raw_calculate_crc(PutBytesStorage *storage, PutBytesCrcType const unsigned int start_address = layout->start_address + layout->start_offset; const unsigned int length = storage->current_offset - layout->start_offset; if (crc_type == PutBytesCrcType_Legacy) { - return flash_calculate_legacy_defective_checksum(start_address, length); + return pbl_flash_legacy_checksum(FLASH, start_address, length); } - return flash_crc32(start_address, length); + return pbl_flash_crc32(FLASH, start_address, length); } void pb_storage_raw_deinit(PutBytesStorage *storage, bool is_success) { diff --git a/src/fw/services/shared_prf_storage/shared_prf_storage.c b/src/fw/services/shared_prf_storage/shared_prf_storage.c index 7d92bb53e4..9ef49753d0 100644 --- a/src/fw/services/shared_prf_storage/shared_prf_storage.c +++ b/src/fw/services/shared_prf_storage/shared_prf_storage.c @@ -5,6 +5,7 @@ #include "pbl/services/shared_prf_storage/v3_sprf/shared_prf_storage_private.h" #include +#include #include "flash_region/flash_region.h" #include "kernel/pbl_malloc.h" #include @@ -75,7 +76,7 @@ static uint32_t prv_current_page_flash_addr(void) { static SprfMagic prv_get_magic_for_page(uint32_t page) { SprfMagic magic; - flash_read_bytes((uint8_t *)&magic, SPRF_PAGE_FLASH_ADDR(page), sizeof(magic)); + pbl_flash_read(FLASH, SPRF_PAGE_FLASH_ADDR(page), (uint8_t *)&magic, sizeof(magic)); return magic; } @@ -132,15 +133,13 @@ static void prv_write_to_current_page(SharedPRFData *data, bool write_metadata) data->magic = SprfMagic_ValidEntry; data->version = SPRF_CUR_VERSION; } - flash_write_bytes((uint8_t *)data, prv_current_page_flash_addr(), sizeof(*data)); + pbl_flash_write(FLASH, prv_current_page_flash_addr(), (uint8_t *)data, sizeof(*data)); } } static void prv_erase_region_and_save(SharedPRFData *data) { - flash_region_erase_optimal_range_no_watchdog(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, - FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, - FLASH_REGION_SHARED_PRF_STORAGE_END, - FLASH_REGION_SHARED_PRF_STORAGE_END); + pbl_flash_erase(FLASH, FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, + FLASH_REGION_SHARED_PRF_STORAGE_END - FLASH_REGION_SHARED_PRF_STORAGE_BEGIN); s_valid_page_idx = 0; prv_write_to_current_page(data, false); } @@ -156,7 +155,7 @@ static void prv_invalidate_current_page(void) { // Invalidate current page SprfMagic new_magic = SprfMagic_InvalidatedEntry; - flash_write_bytes((uint8_t *)&new_magic, prv_current_page_flash_addr(), sizeof(SprfMagic)); + pbl_flash_write(FLASH, prv_current_page_flash_addr(), (uint8_t *)&new_magic, sizeof(SprfMagic)); s_valid_page_idx++; // Sanity check to make sure that the page we are moving to is actually empty. @@ -181,7 +180,7 @@ static void prv_invalidate_current_page(void) { // static void prv_fetch_struct(SharedPRFData *data_out) { - flash_read_bytes((uint8_t *)data_out, prv_current_page_flash_addr(), sizeof(*data_out)); + pbl_flash_read(FLASH, prv_current_page_flash_addr(), (uint8_t *)data_out, sizeof(*data_out)); if (!prv_valid_struct(data_out)) { PBL_LOG_WRN("Shared PRF Storage sector # %"PRIu32" is corrupted. Invalidating" @@ -234,7 +233,7 @@ static void prv_persist_field(uint8_t *field, size_t offset, size_t field_size, // not 0xFFFFFFFF instead of comparing all bytes. *(uint32_t *)field = new_crc; - flash_write_bytes(field, prv_current_page_flash_addr() + offset, field_size); + pbl_flash_write(FLASH, prv_current_page_flash_addr() + offset, field, field_size); cleanup: prv_dealloc_struct(data); @@ -249,7 +248,7 @@ static void prv_erase_field(size_t offset, size_t field_size) { } static bool prv_fetch_field(uint8_t *field_out, size_t offset, size_t field_size) { - flash_read_bytes(field_out, prv_current_page_flash_addr() + offset, field_size); + pbl_flash_read(FLASH, prv_current_page_flash_addr() + offset, field_out, field_size); if (!prv_field_valid(field_out, field_size)) { // If corrupted field, delete entire page PBL_LOG_WRN("Shared PRF Storage sector # %"PRIu32" is corrupted. Invalidating" @@ -295,7 +294,7 @@ void shared_prf_storage_init(void) { page_magic = prv_get_magic_for_page(i); // Check the magic to see if we need to investigate further and read the entire contents. if (page_magic == SprfMagic_ValidEntry || page_magic == SprfMagic_UnpopulatedEntry) { - flash_read_bytes((uint8_t *) &data, SPRF_PAGE_FLASH_ADDR(i), sizeof(data)); + pbl_flash_read(FLASH, SPRF_PAGE_FLASH_ADDR(i), (uint8_t *) &data, sizeof(data)); if (prv_valid_struct(&data)) { s_valid_page_idx = i; break; diff --git a/src/fw/system/bootbits.c b/src/fw/system/bootbits.c index 6a1546bd29..17914eaae0 100644 --- a/src/fw/system/bootbits.c +++ b/src/fw/system/bootbits.c @@ -4,6 +4,7 @@ #include "system/bootbits.h" #include +#include #include #include "flash_region/flash_region.h" #include @@ -141,9 +142,8 @@ uint32_t boot_version_read(void) { struct pb_version version_data; uint32_t version; - flash_read_bytes((uint8_t *)&version_data, - FLASH_REGION_BOOTLOADER_END - sizeof(struct pb_version), - sizeof(struct pb_version)); + pbl_flash_read(FLASH, FLASH_REGION_BOOTLOADER_END - sizeof(struct pb_version), + (uint8_t *)&version_data, sizeof(struct pb_version)); if (version_data.magic != PB_VERSION_MAGIC) { return 0UL; diff --git a/src/fw/system/firmware_storage.c b/src/fw/system/firmware_storage.c index d12016a3f1..b8213ec82c 100644 --- a/src/fw/system/firmware_storage.c +++ b/src/fw/system/firmware_storage.c @@ -10,9 +10,8 @@ #ifndef CONFIG_PBLBOOT FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) { FirmwareDescription firmware_description; - flash_read_bytes((uint8_t*) &firmware_description, firmware_start_address, - sizeof(FirmwareDescription)); - + pbl_flash_read(FLASH, firmware_start_address, (uint8_t *)&firmware_description, + sizeof(FirmwareDescription)); return firmware_description; } @@ -29,7 +28,8 @@ bool firmware_storage_check_valid_firmware_description( PBL_LOG_DBG("CRCing recovery..."); start_address += sizeof(FirmwareDescription); - const uint32_t calculated_crc = flash_crc32(start_address, firmware_description->firmware_length); + const uint32_t calculated_crc = + pbl_flash_crc32(FLASH, start_address, firmware_description->firmware_length); PBL_LOG_DBG("CRCing recovery... done"); @@ -38,7 +38,7 @@ bool firmware_storage_check_valid_firmware_description( #else FirmwareHeader firmware_storage_read_firmware_header(uint32_t address) { FirmwareHeader header; - flash_read_bytes((uint8_t*) &header, address, sizeof(FirmwareHeader)); + pbl_flash_read(FLASH, address, (uint8_t*) &header, sizeof(FirmwareHeader)); return header; } @@ -54,7 +54,8 @@ bool firmware_storage_check_valid_firmware_header( // Log around this operation, as it can take some time (hundreds of ms) PBL_LOG_DBG("CRCing recovery..."); - const uint32_t calculated_crc = flash_crc32(address + header->fw_start, header->fw_length); + const uint32_t calculated_crc = + pbl_flash_crc32(FLASH, address + header->fw_start, header->fw_length); PBL_LOG_DBG("CRCing recovery... done"); @@ -70,10 +71,7 @@ void firmware_storage_invalidate_firmware_slot(uint8_t slot) { slot_start = FLASH_REGION_FIRMWARE_SLOT_1_BEGIN; } - flash_region_erase_optimal_range(slot_start, - slot_start, - slot_start + SUBSECTOR_SIZE_BYTES, - slot_start + SUBSECTOR_SIZE_BYTES); + pbl_flash_erase(FLASH, slot_start, SUBSECTOR_SIZE_BYTES); } #endif \ No newline at end of file diff --git a/src/fw/system/version.c b/src/fw/system/version.c index 2b5ee7f48c..1d2064fa5d 100644 --- a/src/fw/system/version.c +++ b/src/fw/system/version.c @@ -81,7 +81,7 @@ static bool prv_version_copy_flash_fw_metadata(FirmwareMetadata *out_metadata, // The FirmwareMetadata is stored at the end of the binary const uint32_t metadata_offset = flash_address + FIRMWARE_OFFSET + fw_len - sizeof(FirmwareMetadata); - flash_read_bytes((uint8_t*)out_metadata, metadata_offset, sizeof(FirmwareMetadata)); + pbl_flash_read(FLASH, metadata_offset, (uint8_t*)out_metadata, sizeof(FirmwareMetadata)); return true; } diff --git a/tests/fakes/fake_flash_region.c b/tests/fakes/fake_flash_region.c deleted file mode 100644 index f5204832ee..0000000000 --- a/tests/fakes/fake_flash_region.c +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include -#include "flash_region/flash_region.h" - -#include - -static void prv_erase_optimal_range(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end) { - // We want to erase the sector that starts immediately below max_start but after min_start. If no sector - // boundary exists between the two, we need to start erasing sectors after min_start and backfill with - // subsector erases. - int32_t sector_start = (max_start & SECTOR_ADDR_MASK); - int32_t subsector_start = (max_start & SUBSECTOR_ADDR_MASK); - if (sector_start < (int32_t) min_start) { - sector_start += SECTOR_SIZE_BYTES; - } - - // We want to erase ending after min_end but before max_end. If that ends running past the end of max_end, - // we need to erase starting with the sector before and fill in with subsector erases. - int32_t sector_end = ((min_end - 1) & SECTOR_ADDR_MASK) + SECTOR_SIZE_BYTES; - int32_t subsector_end = ((min_end - 1) & SUBSECTOR_ADDR_MASK) + SUBSECTOR_SIZE_BYTES; - if (sector_end > (int32_t) max_end) { - sector_end -= SECTOR_SIZE_BYTES; - } - - int erase_count = 0; - - // Do the upkeep immediately just in case we've spent awhile running without feeding the - // watchdog before doing this erase operation. - - if (sector_start < sector_end) { - // Now erase the leading subsectors... - for (int32_t i = subsector_start; i < sector_start; i += SUBSECTOR_SIZE_BYTES) { - flash_erase_subsector_blocking(i); - } - - // Erase the full sectors... - for (int32_t i = sector_start; i < sector_end; i += SECTOR_SIZE_BYTES) { - flash_erase_sector_blocking(i); - } - - // Erase the trailing subsectors - for (int32_t i = sector_end; i < subsector_end; i += SUBSECTOR_SIZE_BYTES) { - flash_erase_subsector_blocking(i); - } - } else { - // Can't erase any full sectors, just erase subsectors the whole way. - for (int32_t i = subsector_start; i < subsector_end; i += SUBSECTOR_SIZE_BYTES) { - flash_erase_subsector_blocking(i); - } - } -} - -void flash_region_erase_optimal_range(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end) { - prv_erase_optimal_range(min_start, max_start, min_end, max_end); -} - -void flash_region_erase_optimal_range_no_watchdog(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end) { - prv_erase_optimal_range(min_start, max_start, min_end, max_end); -} diff --git a/tests/fakes/fake_spi_flash.c b/tests/fakes/fake_spi_flash.c index 54fdb52951..415cfaa40c 100644 --- a/tests/fakes/fake_spi_flash.c +++ b/tests/fakes/fake_spi_flash.c @@ -3,6 +3,7 @@ #include "fake_spi_flash.h" +#include #include "flash_region/flash_region.h" #include "system/status_codes.h" @@ -125,20 +126,25 @@ void fake_spi_flash_force_future_failure(int after_n_bytes, jmp_buf *retire_to) s_state.jmp_on_failure = retire_to; } -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) { - cl_assert(start_addr >= s_state.offset); - cl_assert(start_addr + buffer_size <= s_state.offset + s_state.length); +static void prv_check_range(uint32_t addr, size_t len) { + cl_assert(addr >= s_state.offset); + cl_assert(addr + len <= s_state.offset + s_state.length); +} - memcpy(buffer, s_state.storage + (start_addr - s_state.offset), buffer_size); +int pbl_flash_read(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len) { + prv_check_range(addr, len); + memcpy(buf, s_state.storage + (addr - s_state.offset), len); + return 0; } -void flash_write_bytes(const uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) { - cl_assert(start_addr >= s_state.offset); - cl_assert(start_addr + buffer_size <= s_state.offset + s_state.length); +int pbl_flash_write(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, + size_t len) { + const uint8_t *src = buf; + prv_check_range(addr, len); ++s_state.write_count; - for (int i = 0; i < buffer_size; ++i) { + for (size_t i = 0; i < len; ++i) { if (s_state.jmp_on_failure != NULL) { if (s_state.bytes_left_till_write_failure == 0) { longjmp(*s_state.jmp_on_failure, 1); @@ -146,45 +152,72 @@ void flash_write_bytes(const uint8_t* buffer, uint32_t start_addr, uint32_t buff s_state.bytes_left_till_write_failure--; } } - // 0 write 0 = 0 - // 1 write 0 = 0 - // 1 write 1 = 1 - // 0 write 1 = 0 - s_state.storage[start_addr - s_state.offset + i] &= buffer[i]; + // Only ones can be turned into zeros + s_state.storage[addr - s_state.offset + i] &= src[i]; } + + return 0; } -//! @param block_size must be a power of two -static void erase_block(uint32_t block_addr, uint32_t block_size) { +static void prv_erase_block(uint32_t addr, uint32_t size) { ++s_state.erase_count; + prv_check_range(addr, size); + memset(&s_state.storage[addr - s_state.offset], 0xff, size); +} - const uint32_t block_mask = ~(block_size - 1); - uint32_t block_start = block_addr & block_mask; +int pbl_flash_erase(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + cl_assert((addr & (SUBSECTOR_SIZE_BYTES - 1)) == 0); + len = (len + SUBSECTOR_SIZE_BYTES - 1) & SUBSECTOR_ADDR_MASK; - cl_assert(block_start >= s_state.offset); - if (block_start + block_size > s_state.offset + s_state.length) { - printf("-0x%x 0x%x\n", block_start + block_size, s_state.offset + s_state.length); + while (len > 0) { + uint32_t unit = SUBSECTOR_SIZE_BYTES; + if ((addr & (SECTOR_SIZE_BYTES - 1)) == 0 && len >= SECTOR_SIZE_BYTES) { + unit = SECTOR_SIZE_BYTES; + } + prv_erase_block(addr, unit); + addr += unit; + len -= unit; } - cl_assert(block_start + block_size <= s_state.offset + s_state.length); - memset(&s_state.storage[block_start - s_state.offset], 0xff, block_size); + return 0; } -void flash_erase_sector_blocking(uint32_t sector_addr) { - erase_block(sector_addr, SECTOR_SIZE_BYTES); +int pbl_flash_erase_async(const struct pbl_flash_device *dev, uint32_t addr, size_t len, + pbl_flash_erase_cb_t cb, void *ctx) { + pbl_flash_erase(dev, addr, len); + cb(ctx, 0); + return 0; } -uint32_t flash_get_subsector_base_address(uint32_t flash_addr) { - return flash_addr & ~(SUBSECTOR_SIZE_BYTES - 1); +bool pbl_flash_is_erased(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + prv_check_range(addr, len); + for (size_t i = 0; i < len; i++) { + if (s_state.storage[addr - s_state.offset + i] != 0xff) { + return false; + } + } + return true; } -void flash_erase_subsector_blocking(uint32_t subsector_addr) { - erase_block(subsector_addr, SUBSECTOR_SIZE_BYTES); +int pbl_flash_protect(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { + return 0; } -uint32_t flash_get_sector_base_address(uint32_t flash_addr) { - return (flash_addr & ~(SECTOR_SIZE_BYTES - 1)); -} +int pbl_flash_unprotect(const struct pbl_flash_device *dev) { return 0; } + +int pbl_flash_init(const struct pbl_flash_device *dev) { return 0; } + +void pbl_flash_stop(const struct pbl_flash_device *dev) {} + +static struct pbl_flash_device_state s_fake_device_state; +static const struct pbl_flash_device s_fake_device = { + .state = &s_fake_device_state, + .base = 0, + .size = UINT32_MAX, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, +}; +const struct pbl_flash_device *const FLASH = &s_fake_device; uint32_t fake_flash_write_count(void) { return s_state.write_count; diff --git a/tests/fw/CMakeLists.txt b/tests/fw/CMakeLists.txt index 80150c661b..235317014a 100644 --- a/tests/fw/CMakeLists.txt +++ b/tests/fw/CMakeLists.txt @@ -17,7 +17,6 @@ pbl_clar_test(test_utf8 pbl_clar_test(test_i18n SOURCES src/fw/services/i18n/i18n.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/resource/resource.c src/fw/resource/resource_storage.c @@ -110,7 +109,6 @@ pbl_clar_test(test_alarm SOURCES src/fw/services/alarms/alarm.c src/fw/services/alarms/alarm_pin.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/util/time/time.c src/fw/util/time/mktime.c @@ -137,7 +135,6 @@ pbl_clar_test(test_alarm_smart SOURCES src/fw/services/alarms/alarm.c src/fw/services/alarms/alarm_pin.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/util/time/time.c src/fw/util/time/mktime.c @@ -173,7 +170,6 @@ pbl_clar_test(test_battery_monitor pbl_clar_test(test_alerts SOURCES - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/filesystem/flash_translation.c src/fw/services/filesystem/pfs.c @@ -199,7 +195,6 @@ pbl_clar_test(test_heap pbl_clar_test(test_flash_logging SOURCES src/fw/debug/flash_logging.c - src/fw/flash_region/flash_region.c tests/fakes/fake_spi_flash.c PLATFORMS asterix DEFINES DUMA_DISABLED PLATFORM_ASTERIX @@ -218,7 +213,6 @@ pbl_clar_test(test_data_logging tests/fakes/fake_spi_flash.c src/fw/util/crc8.c src/fw/util/legacy_checksum.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/filesystem/pfs.c src/fw/services/filesystem/flash_translation.c @@ -278,7 +272,6 @@ pbl_clar_test(test_phone_formatting pbl_clar_test(test_timeline_api SOURCES src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/services/blob_db/pin_db.c src/fw/services/blob_db/timeline_item_storage.c src/fw/services/filesystem/flash_translation.c diff --git a/tests/fw/applib/CMakeLists.txt b/tests/fw/applib/CMakeLists.txt index 2a6632dcfa..4e3c1cf9ef 100644 --- a/tests/fw/applib/CMakeLists.txt +++ b/tests/fw/applib/CMakeLists.txt @@ -37,7 +37,6 @@ pbl_clar_test(test_cpu_cache pbl_clar_test(test_persist SOURCES src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/util/dict.c src/fw/applib/persist.c src/fw/util/rand/rand.c @@ -58,7 +57,6 @@ pbl_clar_test(test_persist pbl_clar_test(test_pbl_std SOURCES - src/fw/flash_region/flash_region.c src/fw/applib/pbl_std/pbl_std.c tests/fakes/fake_rtc.c tests/fakes/fake_spi_flash.c @@ -67,7 +65,6 @@ pbl_clar_test(test_pbl_std pbl_clar_test(test_strftime SOURCES - src/fw/flash_region/flash_region.c src/fw/applib/pbl_std/pbl_std.c src/fw/applib/pbl_std/strftime.c src/fw/applib/pbl_std/timelocal.c @@ -89,7 +86,6 @@ pbl_clar_test(test_text_render pbl_clar_test(test_text_resources SOURCES - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/system/hexdump.c src/fw/applib/fonts/codepoint.c diff --git a/tests/fw/apps/system_apps/health/CMakeLists.txt b/tests/fw/apps/system_apps/health/CMakeLists.txt index 7cdb529084..f86d335721 100644 --- a/tests/fw/apps/system_apps/health/CMakeLists.txt +++ b/tests/fw/apps/system_apps/health/CMakeLists.txt @@ -45,7 +45,6 @@ pbl_clar_test(test_health_card_view src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -152,7 +151,6 @@ pbl_clar_test(test_health_activity_summary_card src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -254,7 +252,6 @@ pbl_clar_test(test_health_sleep_summary_card src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -356,7 +353,6 @@ pbl_clar_test(test_health_hr_summary_card src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -458,7 +454,6 @@ pbl_clar_test(test_health_activity_detail_card src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -559,7 +554,6 @@ pbl_clar_test(test_health_sleep_detail_card src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -660,7 +654,6 @@ pbl_clar_test(test_health_hr_detail_card src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -761,7 +754,6 @@ pbl_clar_test(test_health_detail_card src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/apps/system_apps/launcher/CMakeLists.txt b/tests/fw/apps/system_apps/launcher/CMakeLists.txt index 3f90fe4023..b9a5b8df61 100644 --- a/tests/fw/apps/system_apps/launcher/CMakeLists.txt +++ b/tests/fw/apps/system_apps/launcher/CMakeLists.txt @@ -39,7 +39,6 @@ pbl_clar_test(test_launcher_menu_layer src/fw/applib/ui/window.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/apps/system_apps/music/CMakeLists.txt b/tests/fw/apps/system_apps/music/CMakeLists.txt index 80cec4c8e5..20679a6349 100644 --- a/tests/fw/apps/system_apps/music/CMakeLists.txt +++ b/tests/fw/apps/system_apps/music/CMakeLists.txt @@ -41,7 +41,6 @@ pbl_clar_test(test_music src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/apps/system_apps/timeline/CMakeLists.txt b/tests/fw/apps/system_apps/timeline/CMakeLists.txt index 6773424659..ee331b0681 100644 --- a/tests/fw/apps/system_apps/timeline/CMakeLists.txt +++ b/tests/fw/apps/system_apps/timeline/CMakeLists.txt @@ -45,7 +45,6 @@ pbl_clar_test(test_timeline_list_view src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -169,7 +168,6 @@ pbl_clar_test(test_timeline_no_events src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/apps/system_apps/weather/CMakeLists.txt b/tests/fw/apps/system_apps/weather/CMakeLists.txt index a24a2133c9..4da431915b 100644 --- a/tests/fw/apps/system_apps/weather/CMakeLists.txt +++ b/tests/fw/apps/system_apps/weather/CMakeLists.txt @@ -43,7 +43,6 @@ pbl_clar_test(test_weather_app_layout src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/apps/system_apps/workout/CMakeLists.txt b/tests/fw/apps/system_apps/workout/CMakeLists.txt index f955edbdb5..c23b233d29 100644 --- a/tests/fw/apps/system_apps/workout/CMakeLists.txt +++ b/tests/fw/apps/system_apps/workout/CMakeLists.txt @@ -47,7 +47,6 @@ pbl_clar_test(test_workout_summary src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -153,7 +152,6 @@ pbl_clar_test(test_workout_active src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -257,7 +255,6 @@ pbl_clar_test(test_workout_dialog src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -360,7 +357,6 @@ pbl_clar_test(test_workout_selection src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/apps/watch/kickstart/CMakeLists.txt b/tests/fw/apps/watch/kickstart/CMakeLists.txt index d0c155d74c..3c2a5e71f8 100644 --- a/tests/fw/apps/watch/kickstart/CMakeLists.txt +++ b/tests/fw/apps/watch/kickstart/CMakeLists.txt @@ -40,7 +40,6 @@ pbl_clar_test(test_kickstart src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/drivers/CMakeLists.txt b/tests/fw/drivers/CMakeLists.txt index 4617aa7111..cf754610df 100644 --- a/tests/fw/drivers/CMakeLists.txt +++ b/tests/fw/drivers/CMakeLists.txt @@ -11,13 +11,6 @@ pbl_clar_test(test_qemu_serial pbl_clar_test(test_flash_api SOURCES - src/fw/drivers/flash/flash_api.c - OVERRIDES dummy_board -) - -pbl_clar_test(test_flash_erase - SOURCES - src/fw/drivers/flash/flash_erase.c - PLATFORMS obelix + src/fw/drivers/flash/flash.c OVERRIDES dummy_board ) diff --git a/tests/fw/drivers/test_flash_api.c b/tests/fw/drivers/test_flash_api.c index b7364c78a9..bf861a7785 100644 --- a/tests/fw/drivers/test_flash_api.c +++ b/tests/fw/drivers/test_flash_api.c @@ -1,252 +1,321 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ /* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "fake_new_timer.h" -#include "stubs_sem.h" #include "stubs_analytics.h" -#include "stubs_irq.h" #include "stubs_logging.h" #include "stubs_mutex.h" #include "stubs_passert.h" #include "stubs_pebble_tasks.h" -#include "stubs_prompt.h" +#include "stubs_sem.h" #include "stubs_sleep.h" #include "stubs_task_watchdog.h" -#include "stubs_worker_manager.h" #include -#include -void flash_api_reset_for_test(void); -TimerID flash_api_get_erase_poll_timer_for_test(void); +#include +#include +#define SECTOR_SIZE 0x10000 +#define SUBSECTOR_SIZE 0x1000 -status_t return_success(void) { - return S_SUCCESS; -} +void watchdog_feed(void) {} +void delay_us(uint32_t us) {} -status_t return_error(void) { - return E_ERROR; -} +// Fake driver +/////////////////////////////////////////////////////////// -status_t flash_impl_init(bool coredump_mode) { - return S_SUCCESS; -} - -void flash_impl_use(void) {} -void flash_impl_release_many(uint32_t num_locks) {} +typedef struct EraseCommand { + uint32_t addr; + size_t size; +} EraseCommand; +static EraseCommand s_erase_commands[64]; +static int s_num_erase_commands; +static bool s_blank; +static int s_erase_status_return; +static int s_erase_status_error_count; +static int s_suspend_calls; +static int s_resume_calls; +static int s_read_calls; +static int s_write_calls; -int get_subsector_base_calls = 0; -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - get_subsector_base_calls++; - return addr & 0xffffff00; +static int prv_init(const struct pbl_flash_device *dev) { + return 0; } -int get_sector_base_calls = 0; -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - get_sector_base_calls++; - return addr & 0xfffff000; +static int prv_read(const struct pbl_flash_device *dev, uint32_t addr, void *buf, size_t len) { + s_read_calls++; + memset(buf, s_blank ? 0xff : 0x00, len); + return 0; } -int erase_subsector_begin_calls = 0; -status_t erase_subsector_begin_return = S_SUCCESS; -status_t flash_impl_erase_subsector_begin(FlashAddress addr) { - erase_subsector_begin_calls++; - return erase_subsector_begin_return; +static int prv_write(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, + size_t len) { + s_write_calls++; + return 0; } -int erase_sector_begin_calls = 0; -status_t erase_sector_begin_return = S_SUCCESS; -status_t flash_impl_erase_sector_begin(FlashAddress addr) { - erase_sector_begin_calls++; - return erase_sector_begin_return; +static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, size_t size) { + cl_assert(s_num_erase_commands < (int)(sizeof(s_erase_commands) / sizeof(s_erase_commands[0]))); + s_erase_commands[s_num_erase_commands++] = (EraseCommand){.addr = addr, .size = size}; + return 0; } -int get_erase_status_calls = 0; -status_t (*get_erase_status_fn)(void) = return_success; -status_t flash_impl_get_erase_status(void) { - get_erase_status_calls++; - return get_erase_status_fn(); -} - -int blank_check_subsector_calls = 0; -status_t blank_check_subsector_return = S_FALSE; -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - blank_check_subsector_calls++; - return blank_check_subsector_return; -} - -int blank_check_sector_calls = 0; -status_t blank_check_sector_return = S_FALSE; -status_t flash_impl_blank_check_sector(FlashAddress addr) { - blank_check_sector_calls++; - return blank_check_sector_return; -} - -status_t flash_impl_enter_low_power_mode(void) { - return S_SUCCESS; -} - -status_t flash_impl_exit_low_power_mode(void) { - return S_SUCCESS; -} - -status_t flash_impl_erase_suspend(FlashAddress addr) { - return S_SUCCESS; -} - -status_t flash_impl_erase_resume(FlashAddress addr) { - return S_SUCCESS; -} - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { - return 100; -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { - return 100; -} - -status_t flash_impl_get_write_status(void) { - return E_UNKNOWN; +static int prv_erase_status(const struct pbl_flash_device *dev) { + if (s_erase_status_error_count > 0) { + s_erase_status_error_count--; + return -EIO; + } + return s_erase_status_return; +} + +static int prv_erase_suspend(const struct pbl_flash_device *dev) { + s_suspend_calls++; + return 0; +} + +static int prv_erase_resume(const struct pbl_flash_device *dev) { + s_resume_calls++; + return 0; +} + +static const struct pbl_flash_ops s_sync_ops = { + .init = prv_init, + .read = prv_read, + .write = prv_write, + .erase_begin = prv_erase_begin, +}; + +static const struct pbl_flash_ops s_async_ops = { + .init = prv_init, + .read = prv_read, + .write = prv_write, + .erase_begin = prv_erase_begin, + .erase_status = prv_erase_status, + .erase_suspend = prv_erase_suspend, + .erase_resume = prv_erase_resume, +}; + +static struct pbl_flash_device_state s_state; +static struct pbl_flash_device s_dev = { + .state = &s_state, + .ops = &s_sync_ops, + .base = 0, + .size = 0x100000, + .sector_size = SECTOR_SIZE, + .subsector_size = SUBSECTOR_SIZE, + .sector_erase_ms = 100, + .subsector_erase_ms = 100, +}; + +static void *s_cb_ctx; +static int s_cb_status; +static int s_cb_calls; + +static void prv_cb(void *ctx, int status) { + s_cb_ctx = ctx; + s_cb_status = status; + s_cb_calls++; +} + +static void prv_fire_timers(int max) { + for (int i = 0; i < max; i++) { + TimerID timer = stub_new_timer_get_next(); + if (timer == TIMER_INVALID_ID) { + return; + } + stub_new_timer_fire(timer); + } } -status_t flash_impl_read_sync(void *buffer, FlashAddress addr, size_t len) { - return E_UNKNOWN; -} +// Tests +/////////////////////////////////////////////////////////// -status_t flash_impl_set_burst_mode(bool enable) { - return E_UNKNOWN; +void test_flash_api__initialize(void) { + memset(&s_state, 0, sizeof(s_state)); + s_dev.ops = &s_sync_ops; + s_num_erase_commands = 0; + s_blank = false; + s_erase_status_return = 0; + s_erase_status_error_count = 0; + s_suspend_calls = 0; + s_resume_calls = 0; + s_read_calls = 0; + s_write_calls = 0; + s_cb_ctx = NULL; + s_cb_status = -12345; + s_cb_calls = 0; + + pbl_flash_init(&s_dev); } -status_t flash_impl_unprotect(void) { - return S_SUCCESS; +void test_flash_api__cleanup(void) { + stub_new_timer_cleanup(); } -int flash_impl_write_page_begin(const void *buffer, FlashAddress addr, - size_t len) { - return E_UNKNOWN; +void test_flash_api__erase_subsector(void) { + pbl_flash_erase(&s_dev, 0x3000, SUBSECTOR_SIZE); + cl_assert_equal_i(s_num_erase_commands, 1); + cl_assert_equal_i(s_erase_commands[0].addr, 0x3000); + cl_assert_equal_i(s_erase_commands[0].size, SUBSECTOR_SIZE); +} + +void test_flash_api__erase_sector(void) { + pbl_flash_erase(&s_dev, 0x10000, SECTOR_SIZE); + cl_assert_equal_i(s_num_erase_commands, 1); + cl_assert_equal_i(s_erase_commands[0].addr, 0x10000); + cl_assert_equal_i(s_erase_commands[0].size, SECTOR_SIZE); +} + +void test_flash_api__erase_rounds_length_up(void) { + pbl_flash_erase(&s_dev, 0x0, 1); + cl_assert_equal_i(s_num_erase_commands, 1); + cl_assert_equal_i(s_erase_commands[0].size, SUBSECTOR_SIZE); +} + +void test_flash_api__erase_uses_sectors_where_possible(void) { + // [0xE000, 0x31000): 2 leading subsectors, 2 sectors, 1 trailing subsector + pbl_flash_erase(&s_dev, 0xE000, 0x31000 - 0xE000); + cl_assert_equal_i(s_num_erase_commands, 5); + cl_assert_equal_i(s_erase_commands[0].addr, 0xE000); + cl_assert_equal_i(s_erase_commands[0].size, SUBSECTOR_SIZE); + cl_assert_equal_i(s_erase_commands[1].addr, 0xF000); + cl_assert_equal_i(s_erase_commands[1].size, SUBSECTOR_SIZE); + cl_assert_equal_i(s_erase_commands[2].addr, 0x10000); + cl_assert_equal_i(s_erase_commands[2].size, SECTOR_SIZE); + cl_assert_equal_i(s_erase_commands[3].addr, 0x20000); + cl_assert_equal_i(s_erase_commands[3].size, SECTOR_SIZE); + cl_assert_equal_i(s_erase_commands[4].addr, 0x30000); + cl_assert_equal_i(s_erase_commands[4].size, SUBSECTOR_SIZE); +} + +void test_flash_api__erase_only_subsectors_when_no_full_sector_fits(void) { + pbl_flash_erase(&s_dev, 0x1000, 0x3000); + cl_assert_equal_i(s_num_erase_commands, 3); + for (int i = 0; i < 3; i++) { + cl_assert_equal_i(s_erase_commands[i].addr, 0x1000 + i * SUBSECTOR_SIZE); + cl_assert_equal_i(s_erase_commands[i].size, SUBSECTOR_SIZE); + } } -void flash_impl_enable_write_protection(void) { +void test_flash_api__erase_skips_blank_units(void) { + s_blank = true; + pbl_flash_erase(&s_dev, 0x0, 0x20000); + cl_assert_equal_i(s_num_erase_commands, 0); } -status_t flash_impl_write_protect(FlashAddress start_sector, - FlashAddress end_sector) { - return E_UNKNOWN; +void test_flash_api__erase_async_completes_through_callback(void) { + pbl_flash_erase_async(&s_dev, 0x0, 0x20000, prv_cb, (void *)0x1234); + prv_fire_timers(10); + cl_assert_equal_i(s_num_erase_commands, 2); + cl_assert_equal_i(s_cb_calls, 1); + cl_assert_equal_i(s_cb_status, 0); + cl_assert_equal_p(s_cb_ctx, (void *)0x1234); } -status_t flash_impl_set_nvram_erase_status(bool is_subsector, - FlashAddress addr) { - return S_SUCCESS; +void test_flash_api__erase_async_empty_range(void) { + pbl_flash_erase_async(&s_dev, 0x0, 0, prv_cb, NULL); + cl_assert_equal_i(s_cb_calls, 1); + cl_assert_equal_i(s_cb_status, 0); + cl_assert_equal_i(s_num_erase_commands, 0); } -status_t flash_impl_clear_nvram_erase_status(void) { - return S_SUCCESS; -} +void test_flash_api__async_driver_polls_status(void) { + s_dev.ops = &s_async_ops; + s_erase_status_return = -EBUSY; -status_t flash_impl_get_nvram_erase_status(bool *is_subsector, - FlashAddress *addr) { - return S_FALSE; -} + pbl_flash_erase_async(&s_dev, 0x10000, SECTOR_SIZE, prv_cb, NULL); + cl_assert_equal_i(s_num_erase_commands, 1); + cl_assert_equal_i(s_cb_calls, 0); -status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { - return S_SUCCESS; -} + prv_fire_timers(3); + cl_assert_equal_i(s_cb_calls, 0); -status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { - *locked = false; - return S_SUCCESS; + s_erase_status_return = 0; + prv_fire_timers(1); + cl_assert_equal_i(s_cb_calls, 1); + cl_assert_equal_i(s_cb_status, 0); } -status_t flash_impl_erase_security_register(uint32_t addr) { - return S_SUCCESS; -} +void test_flash_api__retry_erase_on_first_error(void) { + s_dev.ops = &s_async_ops; + s_erase_status_error_count = 1; -status_t flash_impl_write_security_register(uint32_t addr, uint8_t val) { - return S_SUCCESS; + pbl_flash_erase(&s_dev, 0x10000, SECTOR_SIZE); + cl_assert_equal_i(s_num_erase_commands, 2); + cl_assert_equal_i(s_erase_commands[1].addr, 0x10000); } -const FlashSecurityRegisters *flash_impl_security_registers_info(void) { - return NULL; -} +void test_flash_api__handle_uncorrectable_erase_error(void) { + s_dev.ops = &s_async_ops; + s_erase_status_error_count = 100; -void flash_erase_init(void) { + pbl_flash_erase_async(&s_dev, 0x10000, SECTOR_SIZE, prv_cb, NULL); + prv_fire_timers(20); + cl_assert_equal_i(s_cb_calls, 1); + cl_assert_equal_i(s_cb_status, -EIO); + // Initial attempt plus three retries + cl_assert_equal_i(s_num_erase_commands, 4); } +void test_flash_api__read_suspends_and_resumes_erase(void) { + s_dev.ops = &s_async_ops; + s_erase_status_return = -EBUSY; -void *callback_context = NULL; -status_t callback_status = -12345; -static void callback(void *context, status_t status) { - callback_context = context; - callback_status = status; -} - -void test_flash_api__initialize(void) { - callback_context = NULL; - callback_status = -12345; + pbl_flash_erase_async(&s_dev, 0x10000, SECTOR_SIZE, prv_cb, NULL); - get_sector_base_calls = 0; - get_subsector_base_calls = 0; - erase_subsector_begin_calls = 0; - erase_sector_begin_calls = 0; - get_erase_status_calls = 0; - get_erase_status_fn = return_success; - blank_check_subsector_calls = 0; - blank_check_sector_calls = 0; + uint8_t buf[4]; + pbl_flash_read(&s_dev, 0x0, buf, sizeof(buf)); + cl_assert_equal_i(s_suspend_calls, 1); + cl_assert_equal_i(s_resume_calls, 0); + cl_assert(stub_new_timer_is_scheduled(s_state.resume_timer)); - flash_api_reset_for_test(); - flash_init(); -} + // Further reads while suspended do not suspend again + pbl_flash_read(&s_dev, 0x0, buf, sizeof(buf)); + cl_assert_equal_i(s_suspend_calls, 1); -void test_flash_api__cleanup(void) { - stub_new_timer_cleanup(); + stub_new_timer_fire(s_state.resume_timer); + cl_assert_equal_i(s_resume_calls, 1); } -void test_flash_api__erase_subsector_calls_right_impl_func(void) { - flash_erase_subsector(0, callback, NULL); - cl_assert_equal_i(erase_subsector_begin_calls, 1); - cl_assert_equal_i(erase_sector_begin_calls, 0); +void test_flash_api__write_calls_driver(void) { + uint8_t buf[4] = {0}; + pbl_flash_write(&s_dev, 0x100, buf, sizeof(buf)); + cl_assert_equal_i(s_write_calls, 1); } -void test_flash_api__erase_sector_calls_right_impl_func(void) { - flash_erase_sector(0, callback, NULL); - cl_assert_equal_i(erase_sector_begin_calls, 1); - cl_assert_equal_i(erase_subsector_begin_calls, 0); +void test_flash_api__is_erased(void) { + cl_assert(!pbl_flash_is_erased(&s_dev, 0x0, SUBSECTOR_SIZE)); + s_blank = true; + cl_assert(pbl_flash_is_erased(&s_dev, 0x0, SUBSECTOR_SIZE)); } -/////////////////////////////////////////////////////////////////////// +void test_flash_api__erase_fails_in_protected_range(void) { + s_dev.ops = &s_sync_ops; + pbl_flash_protect(&s_dev, 0x10000, SECTOR_SIZE); -status_t erase_status_return_error_once(void) { - get_erase_status_fn = return_success; - return E_ERROR; -} + pbl_flash_erase_async(&s_dev, 0x10000, SECTOR_SIZE, prv_cb, NULL); + cl_assert_equal_i(s_cb_calls, 1); + cl_assert_equal_i(s_cb_status, -EACCES); + cl_assert_equal_i(s_num_erase_commands, 0); -void test_flash_api__retry_erase_on_first_error(void) { - get_erase_status_fn = erase_status_return_error_once; - flash_erase_sector_blocking(0); - cl_assert_equal_i(erase_sector_begin_calls, 2); + pbl_flash_unprotect(&s_dev); + pbl_flash_erase_async(&s_dev, 0x10000, SECTOR_SIZE, prv_cb, NULL); + prv_fire_timers(10); + cl_assert_equal_i(s_cb_calls, 2); + cl_assert_equal_i(s_cb_status, 0); + cl_assert_equal_i(s_num_erase_commands, 1); } -/////////////////////////////////////////////////////////////////////// - -bool uncorrectable_erase_error_cb_called = false; -void uncorrectable_erase_error_cb(void *context, status_t result) { - cl_assert_equal_i(E_ERROR, result); - uncorrectable_erase_error_cb_called = true; -} +void test_flash_api__coredump_mode_is_synchronous(void) { + s_dev.ops = &s_async_ops; + pbl_flash_coredump_init(&s_dev); -void test_flash_api__handle_uncorrectable_erase_error(void) { - get_erase_status_fn = return_error; - TimerID erase_timer = flash_api_get_erase_poll_timer_for_test(); - flash_erase_sector(0, uncorrectable_erase_error_cb, NULL); - int i; - for (i = 0; i < 20 && !uncorrectable_erase_error_cb_called; ++i) { - cl_assert(stub_new_timer_is_scheduled(erase_timer)); - stub_new_timer_fire(erase_timer); - } - cl_assert(i > 1 && i < 20); - cl_assert_equal_i(uncorrectable_erase_error_cb_called, true); + pbl_flash_erase(&s_dev, 0x0, 0x11000); + cl_assert_equal_i(s_num_erase_commands, 2); + cl_assert_equal_i(s_erase_commands[0].size, SECTOR_SIZE); + cl_assert_equal_i(s_erase_commands[1].size, SUBSECTOR_SIZE); + cl_assert_equal_i(s_suspend_calls, 0); } diff --git a/tests/fw/drivers/test_flash_erase.c b/tests/fw/drivers/test_flash_erase.c deleted file mode 100644 index 256a360237..0000000000 --- a/tests/fw/drivers/test_flash_erase.c +++ /dev/null @@ -1,308 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include -#include "pbl/services/new_timer/new_timer.h" - -#include "clar.h" - -#include - -// Stubs -/////////////////////////////////////////////////////////// - -#include "stubs_sem.h" -#include "stubs_logging.h" -#include "stubs_passert.h" - -// Fakes -/////////////////////////////////////////////////////////// - -typedef enum EraseCommandType { - SectorEraseCommand = 1, - SubsectorEraseCommand -} EraseCommandType; - -typedef struct EraseCommand { - uint32_t addr; - EraseCommandType type; -} EraseCommand; - -static EraseCommand s_command_list[32]; -static int s_command_list_index = 0; -static int s_callback_called_count = 0; -static status_t s_callback_status; -static int s_simulate_flash_driver_error_countdown; -static int s_simulate_work_queue_full_countdown; -static bool s_erase_mutex_locked; - -void prv_init_erase_mutex(void) { -} - -void prv_lock_erase_mutex(void) { - cl_assert_equal_i(s_erase_mutex_locked, false); - s_erase_mutex_locked = true; -} - -void prv_unlock_erase_mutex(void) { - s_erase_mutex_locked = false; -} - -void flash_erase_subsector_blocking(uint32_t subsector_addr) { - s_command_list[s_command_list_index++] = (EraseCommand) { - .addr = subsector_addr, - .type = SubsectorEraseCommand - }; -} - -void flash_erase_sector_blocking(uint32_t sector_addr) { - s_command_list[s_command_list_index++] = (EraseCommand) { - .addr = sector_addr, - .type = SectorEraseCommand - }; -} - -void flash_erase_subsector(uint32_t subsector_addr, FlashOperationCompleteCb cb, - void *context) { - s_command_list[s_command_list_index++] = (EraseCommand) { - .addr = subsector_addr, - .type = SubsectorEraseCommand - }; - cl_assert_equal_i(s_erase_mutex_locked, true); - cb(context, (--s_simulate_flash_driver_error_countdown? S_SUCCESS : E_BUSY)); -} - -void flash_erase_sector(uint32_t sector_addr, FlashOperationCompleteCb cb, - void *context) { - s_command_list[s_command_list_index++] = (EraseCommand) { - .addr = sector_addr, - .type = SectorEraseCommand - }; - cl_assert_equal_i(s_erase_mutex_locked, true); - cb(context, (--s_simulate_flash_driver_error_countdown? S_SUCCESS : E_BUSY)); -} - -bool new_timer_add_work_callback(NewTimerWorkCallback cb, void *data) { - if (--s_simulate_work_queue_full_countdown == 0) { - return false; - } - cb(data); - return true; -} - -static void prv_assert_erase_commands(EraseCommand *commands) { - int expected_command_count = 0; - EraseCommand *cursor = commands; - while ((cursor++)->type != 0) { - ++expected_command_count; - } - cl_assert_equal_i(s_command_list_index, expected_command_count); - for (int i = 0; i < s_command_list_index; ++i) { - cl_assert_equal_i(s_command_list[i].addr, commands[i].addr); - cl_assert_equal_i(s_command_list[i].type, commands[i].type); - } -} - -static int s_dummy_value = 42; -static int *s_callback_ctx = &s_dummy_value; - -static void prv_callback(void *context, status_t status) { - cl_assert_equal_p(context, s_callback_ctx); - cl_assert_equal_i(s_erase_mutex_locked, false); - s_callback_status = status; - s_callback_called_count++; -} - -static void prv_assert_callback_called(status_t expected_status) { - cl_assert_equal_i(s_callback_called_count, 1); - cl_assert_equal_i(s_callback_status, expected_status); -} - -static void prv_test_erase_optimal_range( - uint32_t min_start, uint32_t max_start, uint32_t min_end, uint32_t max_end, - EraseCommand *expected_commands) { - flash_erase_optimal_range(min_start, max_start, min_end, max_end, - prv_callback, s_callback_ctx); - prv_assert_erase_commands(expected_commands); - prv_assert_callback_called( - s_command_list_index? S_SUCCESS : S_NO_ACTION_REQUIRED); -} - -// Tests -/////////////////////////////////////////////////////////// - -void test_flash_erase__initialize(void) { - s_command_list_index = 0; - s_callback_called_count = 0; - s_callback_status = 42; - s_simulate_work_queue_full_countdown = -1; - s_simulate_flash_driver_error_countdown = -1; - s_erase_mutex_locked = false; -} - -void test_flash_erase__cleanup(void) { -} - -void test_flash_erase__empty(void) { - prv_test_erase_optimal_range(0, 0, 0, 0, (EraseCommand[]){ { } }); -} - -void test_flash_erase__sectors_simple_1(void) { - // Erase one sector 0x10000 - 0x20000 - prv_test_erase_optimal_range( - 64 * 1024, 64 * 1024, 2 * 64 * 1024, 2 * 64 * 1024, - (EraseCommand[]) { - { 64 * 1024, SectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__sectors_simple_2(void) { - // Erase one sectors 0x10000 - 0x20000 but allow us to erase more - prv_test_erase_optimal_range( - 0, 64 * 1024, 2 * 64 * 1024, 3 * 64 * 1024, - (EraseCommand[]) { - { 64 * 1024, SectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__two_sectors(void) { - // Erase two sectors 0x10000 - 0x30000 but allow us to erase more - prv_test_erase_optimal_range( - 0, 64 * 1024, 3 * 64 * 1024, 4 * 64 * 1024, - (EraseCommand[]) { - { 64 * 1024, SectorEraseCommand }, - { 2 * 64 * 1024, SectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__subsectors_1(void) { - // Offer a less than full sector range but erase the full range - prv_test_erase_optimal_range( - 0, 4 * 1024, 64 * 1024, 64 * 1024, - (EraseCommand[]) { - { 0, SectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__sector_and_subsector(void) { - // Offer a more than a full sector range, needs a sector and a subsector - prv_test_erase_optimal_range( - 60 * 1024, 60 * 1024, 2 * 64 * 1024, 2 * 64 * 1024, - (EraseCommand[]) { - { 60 * 1024, SubsectorEraseCommand }, - { 64 * 1024, SectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__subsectors_on_both_sides(void) { - // Offer a more than a full sector range, needs subsectors on both sides - prv_test_erase_optimal_range( - 60 * 1024, 60 * 1024, ((2 * 64) + 4) * 1024, ((2 * 64) + 8) * 1024, - (EraseCommand[]) { - { 60 * 1024, SubsectorEraseCommand }, - { 64 * 1024, SectorEraseCommand }, - { 2 * 64 * 1024, SubsectorEraseCommand }, - { }, - }); -} - -// Various tests that look like erasing our 96k app resource banks - -void test_flash_erase__96k_app_banks_1(void) { - // App that's in an aligned bank but smaller than 64k - prv_test_erase_optimal_range( - 0, 0, 32 * 1024, 96 * 1024, - (EraseCommand[]) { - { 0, SectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__96k_app_banks_2(void) { - // App that's in an aligned bank but larger than than 64k - prv_test_erase_optimal_range( - 0, 0, 69 * 1024, 96 * 1024, - (EraseCommand[]) { - { 0, SectorEraseCommand }, - { 64 * 1024, SubsectorEraseCommand }, - { 68 * 1024, SubsectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__96k_app_banks_3(void) { - // App that's in an unaligned bank but smaller than 64k - prv_test_erase_optimal_range( - 32 * 1024, 32 * 1024, (32 + 18) * 1024, (32 + 96) * 1024, - (EraseCommand[]) { - { 32 * 1024, SubsectorEraseCommand }, - { 36 * 1024, SubsectorEraseCommand }, - { 40 * 1024, SubsectorEraseCommand }, - { 44 * 1024, SubsectorEraseCommand }, - { 48 * 1024, SubsectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__96k_app_banks_4(void) { - // App that's in an unaligned bank but larger than than 64k - prv_test_erase_optimal_range( - 32 * 1024, 32 * 1024, (32 + 71) * 1024, (32 + 96) * 1024, - (EraseCommand[]) { - { 32 * 1024, SubsectorEraseCommand }, - { 36 * 1024, SubsectorEraseCommand }, - { 40 * 1024, SubsectorEraseCommand }, - { 44 * 1024, SubsectorEraseCommand }, - { 48 * 1024, SubsectorEraseCommand }, - { 52 * 1024, SubsectorEraseCommand }, - { 56 * 1024, SubsectorEraseCommand }, - { 60 * 1024, SubsectorEraseCommand }, - { 64 * 1024, SectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__watch_and_learn(void) { - // Test cases stolen from Alvin's watch and learn app that originally hit this bug - prv_test_erase_optimal_range( - 0x320000, 0x320000, 0x33177c, 0x338000, - (EraseCommand[]) { - { 0x320000, SectorEraseCommand }, - { 0x330000, SubsectorEraseCommand }, - { 0x331000, SubsectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__handle_work_queue_full(void) { - s_simulate_work_queue_full_countdown = 3; - flash_erase_optimal_range( - 32 * 1024, 32 * 1024, (32 + 71) * 1024, (32 + 96) * 1024, - prv_callback, s_callback_ctx); - prv_assert_callback_called(E_INTERNAL); - prv_assert_erase_commands((EraseCommand[]) { - { 32 * 1024, SubsectorEraseCommand }, - { 36 * 1024, SubsectorEraseCommand }, - { 40 * 1024, SubsectorEraseCommand }, - { }, - }); -} - -void test_flash_erase__handle_flash_driver_error(void) { - s_simulate_flash_driver_error_countdown = 3; - flash_erase_optimal_range( - 32 * 1024, 32 * 1024, (32 + 71) * 1024, (32 + 96) * 1024, - prv_callback, s_callback_ctx); - prv_assert_callback_called(E_BUSY); - prv_assert_erase_commands((EraseCommand[]) { - { 32 * 1024, SubsectorEraseCommand }, - { 36 * 1024, SubsectorEraseCommand }, - { 40 * 1024, SubsectorEraseCommand }, - { }, - }); -} diff --git a/tests/fw/graphics/CMakeLists.txt b/tests/fw/graphics/CMakeLists.txt index 3fca21b663..fe1c29fc1f 100644 --- a/tests/fw/graphics/CMakeLists.txt +++ b/tests/fw/graphics/CMakeLists.txt @@ -77,7 +77,6 @@ set(graphics_draw_text_sources src/fw/applib/graphics/arabic_shaping.c src/fw/applib/graphics/perimeter.c src/fw/applib/graphics/text_resources.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/system/hexdump.c src/fw/applib/fonts/codepoint.c @@ -160,7 +159,6 @@ pbl_clar_test(test_graphics_draw_text_flow src/fw/applib/graphics/arabic_shaping.c src/fw/applib/graphics/perimeter.c src/fw/applib/graphics/text_resources.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/system/hexdump.c src/fw/applib/fonts/codepoint.c diff --git a/tests/fw/pebble_actions/CMakeLists.txt b/tests/fw/pebble_actions/CMakeLists.txt index e54a0b3cc6..0b5a6a0657 100644 --- a/tests/fw/pebble_actions/CMakeLists.txt +++ b/tests/fw/pebble_actions/CMakeLists.txt @@ -5,7 +5,6 @@ pbl_clar_test(test_ancs_pebble_actions SOURCES src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/services/blob_db/ios_notif_pref_db.c src/fw/services/filesystem/flash_translation.c src/fw/services/filesystem/pfs.c diff --git a/tests/fw/services/CMakeLists.txt b/tests/fw/services/CMakeLists.txt index ad22a9f1b8..936c1f8589 100644 --- a/tests/fw/services/CMakeLists.txt +++ b/tests/fw/services/CMakeLists.txt @@ -20,7 +20,6 @@ pbl_clar_test(test_pfs src/fw/services/filesystem/flash_translation.c src/fw/services/filesystem/pfs.c src/fw/system/hexdump.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c tests/fakes/fake_spi_flash.c src/fw/util/crc8.c @@ -58,7 +57,6 @@ pbl_clar_test(test_contacts SOURCES tests/fakes/fake_rtc.c tests/fakes/fake_spi_flash.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/contacts/attributes_address.c src/fw/services/contacts/contacts.c @@ -157,7 +155,6 @@ pbl_clar_test(test_timeline_item pbl_clar_test(test_clock SOURCES src/fw/services/i18n/i18n.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/resource/resource.c src/fw/resource/resource_storage.c @@ -192,7 +189,6 @@ pbl_clar_test(test_timezone_database pbl_clar_test(test_wakeup SOURCES - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/util/rand/rand.c third_party/tinymt/TinyMT/tinymt/tinymt32.c @@ -223,7 +219,6 @@ pbl_clar_test(test_migrate_wakeup SOURCES src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/process_management/pebble_process_info.c src/fw/resource/resource.c src/fw/resource/resource_storage.c @@ -289,7 +284,6 @@ pbl_clar_test(test_voice_endpoint pbl_clar_test(test_app_cache SOURCES - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/filesystem/app_file.c src/fw/services/filesystem/flash_translation.c @@ -307,7 +301,6 @@ pbl_clar_test(test_app_cache pbl_clar_test(test_app_install_manager SOURCES - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/resource/resource.c src/fw/resource/resource_storage.c @@ -345,7 +338,6 @@ pbl_clar_test(test_app_menu_data_source src/fw/applib/graphics/gtypes.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/process_management/app_install_manager.c src/fw/process_management/app_menu_data_source.c src/fw/process_management/pebble_process_info.c @@ -397,7 +389,6 @@ pbl_clar_test(test_accel_manager pbl_clar_test(test_shared_prf_storage_v3 SOURCES src/fw/services/shared_prf_storage/shared_prf_storage.c - tests/fakes/fake_flash_region.c tests/fakes/fake_spi_flash.c PLATFORMS obelix OVERRIDES shared_prf_storage_v3 @@ -410,7 +401,6 @@ pbl_clar_test(test_app_file pbl_clar_test(test_do_not_disturb SOURCES - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/filesystem/flash_translation.c src/fw/services/filesystem/pfs.c @@ -434,7 +424,6 @@ pbl_clar_test(test_weather_service src/fw/util/legacy_checksum.c tests/fakes/fake_rtc.c tests/fakes/fake_spi_flash.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/settings/settings_file.c src/fw/services/settings/settings_raw_iter.c diff --git a/tests/fw/services/activity/CMakeLists.txt b/tests/fw/services/activity/CMakeLists.txt index 17b808c00e..163ec71767 100644 --- a/tests/fw/services/activity/CMakeLists.txt +++ b/tests/fw/services/activity/CMakeLists.txt @@ -4,7 +4,6 @@ pbl_clar_test(test_activity SOURCES src/fw/applib/health_service.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/util/base64.c src/fw/util/stats.c @@ -67,7 +66,6 @@ pbl_clar_test(test_kraepelin_algorithm pbl_clar_test(test_activity_algorithm_kraepelin SOURCES - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/util/base64.c src/fw/util/crc8.c diff --git a/tests/fw/services/blob_db/CMakeLists.txt b/tests/fw/services/blob_db/CMakeLists.txt index f32539ca12..4550170792 100644 --- a/tests/fw/services/blob_db/CMakeLists.txt +++ b/tests/fw/services/blob_db/CMakeLists.txt @@ -74,7 +74,6 @@ pbl_clar_test(test_app_db src/fw/util/rand/rand.c third_party/tinymt/TinyMT/tinymt/tinymt32.c tests/fakes/fake_rtc.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/settings/settings_file.c src/fw/services/settings/settings_raw_iter.c @@ -103,7 +102,6 @@ pbl_clar_test(test_contacts_db src/fw/util/rand/rand.c third_party/tinymt/TinyMT/tinymt/tinymt32.c tests/fakes/fake_rtc.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/settings/settings_file.c src/fw/services/settings/settings_raw_iter.c @@ -121,7 +119,6 @@ pbl_clar_test(test_watch_app_prefs_db src/fw/util/rand/rand.c third_party/tinymt/TinyMT/tinymt/tinymt32.c tests/fakes/fake_rtc.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/settings/settings_file.c src/fw/services/settings/settings_raw_iter.c @@ -159,7 +156,6 @@ pbl_clar_test(test_notif_db third_party/tinymt/TinyMT/tinymt/tinymt32.c tests/fakes/fake_rtc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/util/time/time.c src/fw/services/filesystem/flash_translation.c src/fw/services/filesystem/pfs.c @@ -180,7 +176,6 @@ pbl_clar_test(test_weather_db src/fw/util/legacy_checksum.c tests/fakes/fake_rtc.c tests/fakes/fake_spi_flash.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/services/settings/settings_file.c src/fw/services/settings/settings_raw_iter.c @@ -197,7 +192,6 @@ pbl_clar_test(test_prefs_db third_party/tinymt/TinyMT/tinymt/tinymt32.c src/fw/util/time/time.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/services/filesystem/flash_translation.c src/fw/services/filesystem/pfs.c src/fw/services/settings/settings_file.c diff --git a/tests/fw/services/bluetooth/CMakeLists.txt b/tests/fw/services/bluetooth/CMakeLists.txt index dc0c69c772..0ea45fe8eb 100644 --- a/tests/fw/services/bluetooth/CMakeLists.txt +++ b/tests/fw/services/bluetooth/CMakeLists.txt @@ -17,7 +17,6 @@ pbl_clar_test(test_bluetooth_persistent_storage_prf SOURCES src/fw/services/bluetooth/bluetooth_persistent_storage_prf.c src/fw/services/shared_prf_storage/shared_prf_storage.c - tests/fakes/fake_flash_region.c tests/fakes/fake_spi_flash.c tests/fakes/fake_events.c PLATFORMS obelix @@ -31,7 +30,6 @@ pbl_clar_test(test_bluetooth_persistent_storage src/fw/services/settings/settings_raw_iter.c src/fw/services/filesystem/pfs.c src/fw/services/filesystem/flash_translation.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/util/crc8.c src/fw/util/legacy_checksum.c diff --git a/tests/fw/services/notifications/CMakeLists.txt b/tests/fw/services/notifications/CMakeLists.txt index b96173edaf..2fa9da92b1 100644 --- a/tests/fw/services/notifications/CMakeLists.txt +++ b/tests/fw/services/notifications/CMakeLists.txt @@ -14,7 +14,6 @@ pbl_clar_test(test_notification_storage src/fw/services/timeline/attributes_actions.c src/fw/services/timeline/attribute_group.c src/fw/services/timeline/attribute.c - src/fw/flash_region/flash_region.c src/fw/flash_region/filesystem_regions.c src/fw/util/crc8.c src/fw/util/legacy_checksum.c diff --git a/tests/fw/services/settings/CMakeLists.txt b/tests/fw/services/settings/CMakeLists.txt index d198486555..ea08df039f 100644 --- a/tests/fw/services/settings/CMakeLists.txt +++ b/tests/fw/services/settings/CMakeLists.txt @@ -10,7 +10,6 @@ pbl_clar_test(test_settings_file src/fw/util/crc8.c src/fw/util/legacy_checksum.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c tests/fakes/fake_rtc.c src/fw/system/hexdump.c src/fw/services/settings/settings_file.c diff --git a/tests/fw/services/test_pfs.c b/tests/fw/services/test_pfs.c index b585b319d1..a64671cea0 100644 --- a/tests/fw/services/test_pfs.c +++ b/tests/fw/services/test_pfs.c @@ -5,6 +5,7 @@ #include #include +#include "system/status_codes.h" #include "flash_region/flash_region.h" #include "pbl/services/filesystem/pfs.h" #include "pbl/services/filesystem/flash_translation.h" diff --git a/tests/fw/services/test_shared_prf_storage_v3.c b/tests/fw/services/test_shared_prf_storage_v3.c index a886b481d1..e98395fdcd 100644 --- a/tests/fw/services/test_shared_prf_storage_v3.c +++ b/tests/fw/services/test_shared_prf_storage_v3.c @@ -110,7 +110,7 @@ static void prv_fill_flash_random_data(void) { uint8_t *buf = kernel_malloc_check(SPRF_REGION_SIZE); fake_spi_flash_erase(); memset(buf, 0x17, SPRF_REGION_SIZE); - flash_write_bytes(buf, FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, SPRF_REGION_SIZE); + pbl_flash_write(FLASH, FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, buf, SPRF_REGION_SIZE); kernel_free(buf); } @@ -133,7 +133,7 @@ void test_shared_prf_storage_v3__cleanup(void) { void test_shared_prf_storage_v3__init_all_zeros(void) { uint8_t *flash_buf = kernel_zalloc(SPRF_REGION_SIZE); - flash_write_bytes(flash_buf, FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, SPRF_REGION_SIZE); + pbl_flash_write(FLASH, FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, flash_buf, SPRF_REGION_SIZE); shared_prf_storage_init(); shared_prf_storage_set_getting_started_complete(true); cl_assert_equal_b(shared_prf_storage_get_getting_started_complete(), true); @@ -152,13 +152,13 @@ void test_shared_prf_storage_v3__find_first_valid_sector(void) { // Invalidate all entries before it to simulate logging style for (uint32_t j = 0; j < page_idx[i]; j++) { SprfMagic inv_magic = SprfMagic_InvalidatedEntry; - flash_write_bytes((uint8_t *) &inv_magic, SPRF_PAGE_FLASH_OFFSET(j), sizeof(inv_magic)); + pbl_flash_write(FLASH, SPRF_PAGE_FLASH_OFFSET(j), (uint8_t *) &inv_magic, sizeof(inv_magic)); } // Write the valid page - flash_read_bytes((uint8_t *) &data, SPRF_PAGE_FLASH_OFFSET(page_idx[i]), sizeof(data)); + pbl_flash_read(FLASH, SPRF_PAGE_FLASH_OFFSET(page_idx[i]), (uint8_t *) &data, sizeof(data)); data.magic = SprfMagic_ValidEntry; - flash_write_bytes((uint8_t *) &data, SPRF_PAGE_FLASH_OFFSET(page_idx[i]), sizeof(data)); + pbl_flash_write(FLASH, SPRF_PAGE_FLASH_OFFSET(page_idx[i]), (uint8_t *) &data, sizeof(data)); // Call init and see if it found the valid page shared_prf_storage_init(); @@ -417,22 +417,19 @@ void test_shared_prf_storage_v3__handle_corrupt_field_same(void) { cl_assert_equal_i(shared_prf_storage_get_valid_page_number(), 0); SharedPRFData data; - flash_read_bytes((uint8_t *)&data, - SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), - sizeof(data)); + pbl_flash_read(FLASH, SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), + (uint8_t *)&data, sizeof(data)); cl_assert(data.getting_started.crc != 0xFFFFFFFF); uint32_t new_crc = 0; - flash_write_bytes((uint8_t *)&new_crc, - SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()) - + offsetof(SharedPRFData, getting_started) - + offsetof(SprfGettingStarted, crc), - sizeof(new_crc)); + pbl_flash_write(FLASH, + SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()) + + offsetof(SharedPRFData, getting_started) + offsetof(SprfGettingStarted, crc), + (uint8_t *)&new_crc, sizeof(new_crc)); // Confirm new CRC was written - flash_read_bytes((uint8_t *)&data, - SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), - sizeof(data)); + pbl_flash_read(FLASH, SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), + (uint8_t *)&data, sizeof(data)); cl_assert_equal_i(data.getting_started.crc, new_crc); // Should be corrupt, so it should return false @@ -444,11 +441,10 @@ void test_shared_prf_storage_v3__handle_corrupt_field_same(void) { fake_spi_flash_erase(); shared_prf_storage_set_valid_page_number(SPRF_NUM_PAGES - 1); shared_prf_storage_set_getting_started_complete(GETTING_STARTED_COMPLETE); - flash_write_bytes((uint8_t *)&new_crc, - SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()) - + offsetof(SharedPRFData, getting_started) - + offsetof(SprfGettingStarted, crc), - sizeof(new_crc)); + pbl_flash_write(FLASH, + SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()) + + offsetof(SharedPRFData, getting_started) + offsetof(SprfGettingStarted, crc), + (uint8_t *)&new_crc, sizeof(new_crc)); // Should be corrupt, so it should return false cl_assert_equal_b(shared_prf_storage_get_getting_started_complete(), false); // Should have moved to the next page, which is ZERO since we had to wrap around. @@ -463,22 +459,18 @@ void test_shared_prf_storage_v3__handle_corrupt_field_during_setting(void) { cl_assert_equal_i(shared_prf_storage_get_valid_page_number(), 0); SharedPRFData data; - flash_read_bytes((uint8_t *)&data, - SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), - sizeof(data)); + pbl_flash_read(FLASH, SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), + (uint8_t *)&data, sizeof(data)); cl_assert(data.getting_started.crc != 0xFFFFFFFF); uint32_t new_crc = 0; - flash_write_bytes((uint8_t *)&new_crc, - SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()) + pbl_flash_write(FLASH, SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()) + offsetof(SharedPRFData, ble_pairing_data) - + offsetof(SprfBlePairingData, crc), - sizeof(new_crc)); + + offsetof(SprfBlePairingData, crc), (uint8_t *)&new_crc, sizeof(new_crc)); // Confirm new CRC was written - flash_read_bytes((uint8_t *)&data, - SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), - sizeof(data)); + pbl_flash_read(FLASH, SPRF_PAGE_FLASH_OFFSET(shared_prf_storage_get_valid_page_number()), + (uint8_t *)&data, sizeof(data)); cl_assert_equal_i(data.ble_pairing_data.crc, new_crc); // Should be corrupt, so after a 'set', the page number should increment even though we are diff --git a/tests/fw/services/timeline/CMakeLists.txt b/tests/fw/services/timeline/CMakeLists.txt index ab6d4464ab..4cb1717897 100644 --- a/tests/fw/services/timeline/CMakeLists.txt +++ b/tests/fw/services/timeline/CMakeLists.txt @@ -7,7 +7,6 @@ pbl_clar_test(test_reminders tests/fakes/fake_spi_flash.c src/fw/util/rand/rand.c third_party/tinymt/TinyMT/tinymt/tinymt32.c - src/fw/flash_region/flash_region.c src/fw/util/time/time.c src/fw/flash_region/filesystem_regions.c src/fw/services/blob_db/timeline_item_storage.c @@ -78,7 +77,6 @@ pbl_clar_test(test_timeline_model third_party/tinymt/TinyMT/tinymt/tinymt32.c tests/fakes/fake_rtc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/services/blob_db/timeline_item_storage.c src/fw/services/settings/settings_file.c src/fw/services/settings/settings_raw_iter.c @@ -172,7 +170,6 @@ pbl_clar_test(test_timeline_layouts src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/fw/system/CMakeLists.txt b/tests/fw/system/CMakeLists.txt deleted file mode 100644 index 3dd593442e..0000000000 --- a/tests/fw/system/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Core Devices LLC -# SPDX-License-Identifier: Apache-2.0 - -pbl_clar_test(test_flash_region - SOURCES - src/fw/flash_region/flash_region.c - PLATFORMS obelix - OVERRIDES dummy_board -) diff --git a/tests/fw/system/test_flash_region.c b/tests/fw/system/test_flash_region.c deleted file mode 100644 index dc0edec879..0000000000 --- a/tests/fw/system/test_flash_region.c +++ /dev/null @@ -1,227 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Google LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include "flash_region/flash_region.h" - -#include "clar.h" - -// Stubs -/////////////////////////////////////////////////////////// - -#include "stubs_logging.h" -#include "stubs_pebble_tasks.h" -#include "stubs_passert.h" -#include "stubs_sleep.h" -#include "stubs_task_watchdog.h" - -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) {} -void flash_write_bytes(const uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) {} - -// Fakes -/////////////////////////////////////////////////////////// - -typedef enum EraseCommandType { - SectorEraseCommand, - SubsectorEraseCommand -} EraseCommandType; - -typedef struct EraseCommand { - uint32_t addr; - EraseCommandType type; -} EraseCommand; - -static EraseCommand s_command_list[32]; -static int s_command_list_index = 0; - -void flash_erase_subsector_blocking(uint32_t subsector_addr) { - s_command_list[s_command_list_index++] = (EraseCommand) { .addr = subsector_addr, .type = SubsectorEraseCommand }; -} - -void flash_erase_sector_blocking(uint32_t subsector_addr) { - s_command_list[s_command_list_index++] = (EraseCommand) { .addr = subsector_addr, .type = SectorEraseCommand }; -} - -// Tests -/////////////////////////////////////////////////////////// - -void test_flash_region__initialize(void) { - s_command_list_index = 0; -} - -void test_flash_region__cleanup(void) { -} - -void test_flash_region__erase_optimal_range_empty(void) { - flash_region_erase_optimal_range(0, 0, 0, 0); - - cl_assert_equal_i(s_command_list_index, 0); -} - -void test_flash_region__erase_optimal_range_sectors_simple(void) { - // Erase one sector 0x10000 - 0x20000 - flash_region_erase_optimal_range(64 * 1024, 64 * 1024, 2 * 64 * 1024, 2 * 64 * 1024); - - cl_assert_equal_i(s_command_list_index, 1); - cl_assert_equal_i(s_command_list[0].addr, 64 * 1024); - cl_assert_equal_i(s_command_list[0].type, SectorEraseCommand); - - s_command_list_index = 0; - - // Erase one sectors 0x10000 - 0x20000 but allow us to erase more - flash_region_erase_optimal_range(0, 64 * 1024, 2 * 64 * 1024, 3 * 64 * 1024); - - cl_assert_equal_i(s_command_list_index, 1); - cl_assert_equal_i(s_command_list[0].addr, 64 * 1024); - cl_assert_equal_i(s_command_list[0].type, SectorEraseCommand); - - s_command_list_index = 0; - - // Erase two sectors 0x10000 - 0x30000 but allow us to erase more - flash_region_erase_optimal_range(0, 64 * 1024, 3 * 64 * 1024, 4 * 64 * 1024); - - cl_assert_equal_i(s_command_list_index, 2); - - cl_assert_equal_i(s_command_list[0].addr, 64 * 1024); - cl_assert_equal_i(s_command_list[0].type, SectorEraseCommand); - - cl_assert_equal_i(s_command_list[1].addr, 2 * 64 * 1024); - cl_assert_equal_i(s_command_list[1].type, SectorEraseCommand); -} - -void test_flash_region__erase_optimal_range_subsectors(void) { - // Offer a less than full sector range but erase the full range - flash_region_erase_optimal_range(0, 4 * 1024, 64 * 1024, 64 * 1024); - - cl_assert_equal_i(s_command_list_index, 1); - cl_assert_equal_i(s_command_list[0].addr, 0); - cl_assert_equal_i(s_command_list[0].type, SectorEraseCommand); - - s_command_list_index = 0; - - // Offer a more than a full sector range, needs a sector and a subsector - flash_region_erase_optimal_range(60 * 1024, 60 * 1024, 2 * 64 * 1024, 2 * 64 * 1024); - - cl_assert_equal_i(s_command_list_index, 2); - cl_assert_equal_i(s_command_list[0].addr, 60 * 1024); - cl_assert_equal_i(s_command_list[0].type, SubsectorEraseCommand); - cl_assert_equal_i(s_command_list[1].addr, 64 * 1024); - cl_assert_equal_i(s_command_list[1].type, SectorEraseCommand); - - s_command_list_index = 0; - - // Offer a more than a full sector range, needs subsectors on both sides - flash_region_erase_optimal_range(60 * 1024, 60 * 1024, ((2 * 64) + 4) * 1024, ((2 * 64) + 8) * 1024); - - cl_assert_equal_i(s_command_list_index, 3); - cl_assert_equal_i(s_command_list[0].addr, 60 * 1024); - cl_assert_equal_i(s_command_list[0].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[1].addr, 64 * 1024); - cl_assert_equal_i(s_command_list[1].type, SectorEraseCommand); - - cl_assert_equal_i(s_command_list[2].addr, 2 * 64 * 1024); - cl_assert_equal_i(s_command_list[2].type, SubsectorEraseCommand); -} - -void test_flash_region__erase_optimal_range_96k_app_banks(void) { - // Various tests that look like erasing our 96k app resource banks - - // App that's in an aligned bank but smaller than 64k - flash_region_erase_optimal_range(0, 0, 32 * 1024, 96 * 1024); - - cl_assert_equal_i(s_command_list_index, 1); - cl_assert_equal_i(s_command_list[0].addr, 0); - cl_assert_equal_i(s_command_list[0].type, SectorEraseCommand); - - s_command_list_index = 0; - - // App that's in an aligned bank but larger than than 64k - flash_region_erase_optimal_range(0, 0, 69 * 1024, 96 * 1024); - - cl_assert_equal_i(s_command_list_index, 3); - cl_assert_equal_i(s_command_list[0].addr, 0); - cl_assert_equal_i(s_command_list[0].type, SectorEraseCommand); - - cl_assert_equal_i(s_command_list[1].addr, 64 * 1024); - cl_assert_equal_i(s_command_list[1].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[2].addr, 68 * 1024); - cl_assert_equal_i(s_command_list[2].type, SubsectorEraseCommand); - - s_command_list_index = 0; - - // App that's in an unaligned bank but smaller than 64k - flash_region_erase_optimal_range(32 * 1024, 32 * 1024, (32 + 18) * 1024, (32 + 96) * 1024); - - cl_assert_equal_i(s_command_list_index, 5); - - cl_assert_equal_i(s_command_list[0].addr, 32 * 1024); - cl_assert_equal_i(s_command_list[0].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[1].addr, 36 * 1024); - cl_assert_equal_i(s_command_list[1].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[2].addr, 40 * 1024); - cl_assert_equal_i(s_command_list[2].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[3].addr, 44 * 1024); - cl_assert_equal_i(s_command_list[3].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[4].addr, 48 * 1024); - cl_assert_equal_i(s_command_list[4].type, SubsectorEraseCommand); - - s_command_list_index = 0; - - // App that's in an unaligned bank but larger than than 64k - flash_region_erase_optimal_range(32 * 1024, 32 * 1024, (32 + 71) * 1024, (32 + 96) * 1024); - - cl_assert_equal_i(s_command_list_index, 9); - - cl_assert_equal_i(s_command_list[0].addr, 32 * 1024); - cl_assert_equal_i(s_command_list[0].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[1].addr, 36 * 1024); - cl_assert_equal_i(s_command_list[1].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[2].addr, 40 * 1024); - cl_assert_equal_i(s_command_list[2].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[3].addr, 44 * 1024); - cl_assert_equal_i(s_command_list[3].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[4].addr, 48 * 1024); - cl_assert_equal_i(s_command_list[4].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[5].addr, 52 * 1024); - cl_assert_equal_i(s_command_list[5].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[6].addr, 56 * 1024); - cl_assert_equal_i(s_command_list[6].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[7].addr, 60 * 1024); - cl_assert_equal_i(s_command_list[7].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[8].addr, 64 * 1024); - cl_assert_equal_i(s_command_list[8].type, SectorEraseCommand); - - s_command_list_index = 0; - -} - -void test_flash_region__erase_optimal_range_watch_and_learn(void) { - // Test cases stolen from Alvin's watch and learn app that originally hit this bug - - flash_region_erase_optimal_range(0x320000, 0x320000, 0x33177c, 0x338000); - - cl_assert_equal_i(s_command_list_index, 3); - - cl_assert_equal_i(s_command_list[0].addr, 0x320000); - cl_assert_equal_i(s_command_list[0].type, SectorEraseCommand); - - cl_assert_equal_i(s_command_list[1].addr, 0x330000); - cl_assert_equal_i(s_command_list[1].type, SubsectorEraseCommand); - - cl_assert_equal_i(s_command_list[2].addr, 0x331000); - cl_assert_equal_i(s_command_list[2].type, SubsectorEraseCommand); -} - diff --git a/tests/fw/ui/CMakeLists.txt b/tests/fw/ui/CMakeLists.txt index 26da489ff5..bae166c8f7 100644 --- a/tests/fw/ui/CMakeLists.txt +++ b/tests/fw/ui/CMakeLists.txt @@ -296,7 +296,6 @@ pbl_clar_test(test_menu_layer_system_cells src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -369,7 +368,6 @@ pbl_clar_test(test_action_menu_window src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -451,7 +449,6 @@ pbl_clar_test(test_notification_window src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -546,7 +543,6 @@ pbl_clar_test(test_simple_dialog src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -633,7 +629,6 @@ pbl_clar_test(test_emoji_fonts src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -703,7 +698,6 @@ pbl_clar_test(test_expandable_dialog src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -800,7 +794,6 @@ pbl_clar_test(test_selection_windows src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -896,7 +889,6 @@ pbl_clar_test(test_timeline_peek src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c @@ -994,7 +986,6 @@ pbl_clar_test(test_option_menu_window src/fw/board/displays/display_getafix.c src/fw/drivers/flash/flash_crc.c src/fw/flash_region/filesystem_regions.c - src/fw/flash_region/flash_region.c src/fw/resource/resource.c src/fw/resource/resource_storage.c src/fw/resource/resource_storage_builtin.c diff --git a/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h b/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h index ae5907f8c4..132440b8d1 100644 --- a/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h +++ b/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h @@ -12,9 +12,3 @@ #define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN 0x0 #define FLASH_REGION_SHARED_PRF_STORAGE_END 0x1000 - -void flash_region_erase_optimal_range(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end); - -void flash_region_erase_optimal_range_no_watchdog(uint32_t min_start, uint32_t max_start, - uint32_t min_end, uint32_t max_end); From 923c92d3d2ef15a03a991934d0c06c38ef62e649 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Tue, 8 Sep 2026 19:38:15 +0200 Subject: [PATCH 3/3] drivers/flash: instantiate devices in the drivers via Kconfig Boards no longer define the flash device. Each driver builds its own instance from Kconfig and exports it as FLASH, in the direction of Zephyr-style driver-owned instantiation: - nrf5_qspi.c takes the clock frequency, pins and read/program commands from FLASH_NRF5_QSPI_*. - sf32lb52_mpi.c takes the MPI instance, memory window size, clock divider and DMA channel/request from FLASH_SF32LB52_MPI_*. - qemu.c takes the flash size from FLASH_QEMU_SIZE. The NOR part becomes a Kconfig choice (FLASH_NOR_PART) and the part table now carries the geometry and erase timings as a struct pbl_flash_geometry, which the device points at, so a driver needs no board input beyond the part selection and its controller settings. The SF32 deep power-down latencies come from the part table too. The driver structs move into the .c files; only the SF32 deep power-down entry points remain in a public header, for the SoC idle code. Co-Authored-By: Claude Fable 5.1 Signed-off-by: Gerard Marull-Paretas --- boards/asterix/defconfig | 7 ++ boards/getafix/defconfig | 2 + boards/obelix/defconfig | 2 + include/pbl/drivers/flash.h | 19 ++-- include/pbl/drivers/flash/nor_part.h | 9 +- include/pbl/drivers/flash/nrf5_qspi.h | 44 -------- include/pbl/drivers/flash/qemu.h | 16 --- include/pbl/drivers/flash/sf32lb52_mpi.h | 26 ----- src/fw/board/boards/board_asterix.c | 32 ------ src/fw/board/boards/board_getafix.c | 35 ------- src/fw/board/boards/board_obelix.c | 35 ------- src/fw/board/boards/board_qemu_emery.c | 17 --- src/fw/board/boards/board_qemu_flint.c | 17 --- src/fw/board/boards/board_qemu_gabbro.c | 17 --- src/fw/drivers/flash/Kconfig | 128 +++++++++++++++++++++-- src/fw/drivers/flash/README.md | 13 ++- src/fw/drivers/flash/flash.c | 18 ++-- src/fw/drivers/flash/gd25lq255e.c | 10 +- src/fw/drivers/flash/gd25q256e.c | 10 +- src/fw/drivers/flash/nrf5_qspi.c | 119 +++++++++++++++++---- src/fw/drivers/flash/qemu.c | 32 +++++- src/fw/drivers/flash/sf32lb52_mpi.c | 80 +++++++++++--- tests/fakes/fake_spi_flash.c | 10 +- tests/fw/drivers/test_flash_api.c | 14 ++- 24 files changed, 400 insertions(+), 312 deletions(-) delete mode 100644 include/pbl/drivers/flash/nrf5_qspi.h delete mode 100644 include/pbl/drivers/flash/qemu.h diff --git a/boards/asterix/defconfig b/boards/asterix/defconfig index bda744b367..a5dbb5f1d6 100644 --- a/boards/asterix/defconfig +++ b/boards/asterix/defconfig @@ -36,6 +36,13 @@ CONFIG_DISPLAY=y CONFIG_FLASH=y CONFIG_FLASH_GD25LQ255E=y +CONFIG_FLASH_NRF5_QSPI_CLK_FREQ_HZ=8000000 +CONFIG_FLASH_NRF5_QSPI_SCK_PIN=19 +CONFIG_FLASH_NRF5_QSPI_CSN_PIN=17 +CONFIG_FLASH_NRF5_QSPI_IO0_PIN=20 +CONFIG_FLASH_NRF5_QSPI_IO1_PIN=21 +CONFIG_FLASH_NRF5_QSPI_IO2_PIN=22 +CONFIG_FLASH_NRF5_QSPI_IO3_PIN=23 CONFIG_OTP=y CONFIG_OTP_FLASH=y diff --git a/boards/getafix/defconfig b/boards/getafix/defconfig index 998e184719..f47b218150 100644 --- a/boards/getafix/defconfig +++ b/boards/getafix/defconfig @@ -39,6 +39,8 @@ CONFIG_DISPLAY=y CONFIG_FLASH=y CONFIG_FLASH_GD25Q256E=y +CONFIG_FLASH_SF32LB52_MPI_INSTANCE=2 +CONFIG_FLASH_SF32LB52_MPI_MSIZE=32 CONFIG_OTP=y CONFIG_OTP_FLASH=y diff --git a/boards/obelix/defconfig b/boards/obelix/defconfig index 88b64bd51f..3e90bd80dd 100644 --- a/boards/obelix/defconfig +++ b/boards/obelix/defconfig @@ -44,6 +44,8 @@ CONFIG_DISPLAY=y CONFIG_FLASH=y CONFIG_FLASH_GD25Q256E=y +CONFIG_FLASH_SF32LB52_MPI_INSTANCE=2 +CONFIG_FLASH_SF32LB52_MPI_MSIZE=16 CONFIG_OTP=y CONFIG_OTP_FLASH=y diff --git a/include/pbl/drivers/flash.h b/include/pbl/drivers/flash.h index a3314d7143..7231c14b67 100644 --- a/include/pbl/drivers/flash.h +++ b/include/pbl/drivers/flash.h @@ -15,6 +15,17 @@ struct pbl_flash_device; typedef void (*pbl_flash_erase_cb_t)(void *ctx, int status); +struct pbl_flash_geometry { + uint32_t size; + uint32_t page_size; + //! Large and small erase units. + uint32_t sector_size; + uint32_t subsector_size; + //! Typical erase durations, used to pace polling of asynchronous erases. + uint16_t sector_erase_ms; + uint16_t subsector_erase_ms; +}; + //! One-time-programmable "security registers" of NOR parts that have them. struct pbl_flash_sec_regs { const uint32_t *addrs; @@ -79,13 +90,7 @@ struct pbl_flash_device { const struct pbl_flash_ops *ops; //! Address of the first byte, as seen by the flash API. uint32_t base; - uint32_t size; - //! Large and small erase units. - uint32_t sector_size; - uint32_t subsector_size; - //! Typical erase durations, used to pace polling of asynchronous erases. - uint16_t sector_erase_ms; - uint16_t subsector_erase_ms; + const struct pbl_flash_geometry *geometry; //! NULL when the part has no security registers. const struct pbl_flash_sec_regs *sec_regs; }; diff --git a/include/pbl/drivers/flash/nor_part.h b/include/pbl/drivers/flash/nor_part.h index d325769513..eb919fddd3 100644 --- a/include/pbl/drivers/flash/nor_part.h +++ b/include/pbl/drivers/flash/nor_part.h @@ -23,7 +23,7 @@ enum pbl_flash_nor_qer { struct pbl_flash_nor_part { const char *name; uint32_t id; - uint32_t size; + struct pbl_flash_geometry geometry; struct { uint8_t write_enable; uint8_t rdsr1; @@ -56,3 +56,10 @@ struct pbl_flash_nor_part { extern const struct pbl_flash_nor_part pbl_flash_nor_gd25lq255e; extern const struct pbl_flash_nor_part pbl_flash_nor_gd25q256e; + +//! The part selected by Kconfig for the board's flash. +#if defined(CONFIG_FLASH_GD25LQ255E) +#define PBL_FLASH_NOR_PART pbl_flash_nor_gd25lq255e +#elif defined(CONFIG_FLASH_GD25Q256E) +#define PBL_FLASH_NOR_PART pbl_flash_nor_gd25q256e +#endif diff --git a/include/pbl/drivers/flash/nrf5_qspi.h b/include/pbl/drivers/flash/nrf5_qspi.h deleted file mode 100644 index fb4f28f8ac..0000000000 --- a/include/pbl/drivers/flash/nrf5_qspi.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include - -#include -#include -#include "pbl/kernel/sem.h" - -enum pbl_flash_nrf5_qspi_read_mode { - PBL_FLASH_NRF5_QSPI_READ_FASTREAD, - PBL_FLASH_NRF5_QSPI_READ_READ2O, - PBL_FLASH_NRF5_QSPI_READ_READ2IO, - PBL_FLASH_NRF5_QSPI_READ_READ4O, - PBL_FLASH_NRF5_QSPI_READ_READ4IO, -}; - -enum pbl_flash_nrf5_qspi_write_mode { - PBL_FLASH_NRF5_QSPI_WRITE_PP, - PBL_FLASH_NRF5_QSPI_WRITE_PP2O, - PBL_FLASH_NRF5_QSPI_WRITE_PP4O, - PBL_FLASH_NRF5_QSPI_WRITE_PP4IO, -}; - -struct pbl_flash_nrf5_qspi_state { - struct pbl_flash_device_state flash; - struct pbl_sem sem; - bool initialized; -}; - -struct pbl_flash_nrf5_qspi { - struct pbl_flash_device dev; - const struct pbl_flash_nor_part *part; - uint32_t clk_freq_hz; - uint32_t cs_gpio; - uint32_t clk_gpio; - uint32_t data_gpio[4]; - enum pbl_flash_nrf5_qspi_read_mode read_mode; - enum pbl_flash_nrf5_qspi_write_mode write_mode; -}; - -extern const struct pbl_flash_ops pbl_flash_nrf5_qspi_ops; diff --git a/include/pbl/drivers/flash/qemu.h b/include/pbl/drivers/flash/qemu.h deleted file mode 100644 index 8580717dcf..0000000000 --- a/include/pbl/drivers/flash/qemu.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ -/* SPDX-License-Identifier: Apache-2.0 */ - -#pragma once - -#include - -#include - -struct pbl_flash_qemu { - struct pbl_flash_device dev; - //! Controller registers of the QEMU pebble-extflash device. - uintptr_t regs; -}; - -extern const struct pbl_flash_ops pbl_flash_qemu_ops; diff --git a/include/pbl/drivers/flash/sf32lb52_mpi.h b/include/pbl/drivers/flash/sf32lb52_mpi.h index 45bd32dfd4..7c926b6242 100644 --- a/include/pbl/drivers/flash/sf32lb52_mpi.h +++ b/include/pbl/drivers/flash/sf32lb52_mpi.h @@ -3,34 +3,8 @@ #pragma once -#include -#include - #include -#include - -struct pbl_flash_sf32lb52_mpi_state { - struct pbl_flash_device_state flash; - QSPI_FLASH_CTX_T ctx; - DMA_HandleTypeDef hdma; - qspi_configure_t cfg; - struct dma_config dma; - bool initialized; -}; - -struct pbl_flash_sf32lb52_mpi { - struct pbl_flash_device dev; - const char *name; - uint32_t id; - uint16_t clk_div; - //! Deep power-down entry / exit latencies of the part. - uint32_t dpd_enter_us; - uint32_t dpd_exit_us; -}; - -extern const struct pbl_flash_ops pbl_flash_sf32lb52_mpi_ops; - //! Deep power-down around SoC deep sleep. Called with interrupts disabled. void pbl_flash_sf32lb52_mpi_dpd_enter(const struct pbl_flash_device *dev); void pbl_flash_sf32lb52_mpi_dpd_exit(const struct pbl_flash_device *dev); diff --git a/src/fw/board/boards/board_asterix.c b/src/fw/board/boards/board_asterix.c index 853230d030..f4996407db 100644 --- a/src/fw/board/boards/board_asterix.c +++ b/src/fw/board/boards/board_asterix.c @@ -4,9 +4,7 @@ #include #include "board/board.h" -#include "flash_region/flash_region.h" #include -#include #include #include #include @@ -16,43 +14,13 @@ #include #include #include -#include "flash_region/flash_region.h" -// QSPI #include #include #include #include #include -static struct pbl_flash_nrf5_qspi_state s_flash_state; -static const struct pbl_flash_nrf5_qspi s_flash = { - .dev = - { - .state = &s_flash_state.flash, - .ops = &pbl_flash_nrf5_qspi_ops, - .size = BOARD_NOR_FLASH_SIZE, - .sector_size = SECTOR_SIZE_BYTES, - .subsector_size = SUBSECTOR_SIZE_BYTES, - .sector_erase_ms = 150, - .subsector_erase_ms = 50, - .sec_regs = &pbl_flash_nor_gd25lq255e.sec_regs, - }, - .part = &pbl_flash_nor_gd25lq255e, - .clk_freq_hz = 8000000UL, - .cs_gpio = NRF_GPIO_PIN_MAP(0, 17), - .clk_gpio = NRF_GPIO_PIN_MAP(0, 19), - .data_gpio = - { - NRF_GPIO_PIN_MAP(0, 20), - NRF_GPIO_PIN_MAP(0, 21), - NRF_GPIO_PIN_MAP(0, 22), - NRF_GPIO_PIN_MAP(0, 23), - }, - .read_mode = PBL_FLASH_NRF5_QSPI_READ_READ4IO, - .write_mode = PBL_FLASH_NRF5_QSPI_WRITE_PP4O, -}; -const struct pbl_flash_device *const FLASH = &s_flash.dev; /* PERIPHERAL ID 43 */ static UARTDeviceState s_dbg_uart_state; diff --git a/src/fw/board/boards/board_getafix.c b/src/fw/board/boards/board_getafix.c index b8b4b3136e..e33b02d6f4 100644 --- a/src/fw/board/boards/board_getafix.c +++ b/src/fw/board/boards/board_getafix.c @@ -2,9 +2,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" -#include "flash_region/flash_region.h" -#include -#include #include "board/splash.h" #include #include "system/passert.h" @@ -78,38 +75,6 @@ static UARTDevice HCI_TRACE_UART_DEVICE = { UARTDevice *const HCI_TRACE_UART = &HCI_TRACE_UART_DEVICE; #endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY -static struct pbl_flash_sf32lb52_mpi_state s_flash_state = { - .cfg = { - .Instance = FLASH2, - .line = HAL_FLASH_QMODE, - .base = FLASH2_BASE_ADDR, - .msize = 32, - .SpiMode = SPI_MODE_NOR, - }, - .dma = { - .Instance = DMA1_Channel2, - .dma_irq = DMAC1_CH2_IRQn, - .request = DMA_REQUEST_1, - }, -}; -static const struct pbl_flash_sf32lb52_mpi s_flash = { - .dev = - { - .state = &s_flash_state.flash, - .ops = &pbl_flash_sf32lb52_mpi_ops, - .base = FLASH_REGION_BASE_ADDRESS, - .size = BOARD_NOR_FLASH_SIZE, - .sector_size = SECTOR_SIZE_BYTES, - .subsector_size = SUBSECTOR_SIZE_BYTES, - .sec_regs = &pbl_flash_nor_gd25q256e.sec_regs, - }, - .name = "GD25Q256E", - .id = 0x1940c8, - .clk_div = 0U, - .dpd_enter_us = 3, - .dpd_exit_us = 20, -}; -const struct pbl_flash_device *const FLASH = &s_flash.dev; static DisplayJDIState s_display_state = { .hlcdc = { diff --git a/src/fw/board/boards/board_obelix.c b/src/fw/board/boards/board_obelix.c index 9256d04609..c4fe3cf99f 100644 --- a/src/fw/board/boards/board_obelix.c +++ b/src/fw/board/boards/board_obelix.c @@ -2,9 +2,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" -#include "flash_region/flash_region.h" -#include -#include #include "board/splash.h" #include #include @@ -197,38 +194,6 @@ static UARTDevice HCI_TRACE_UART_DEVICE = { UARTDevice *const HCI_TRACE_UART = &HCI_TRACE_UART_DEVICE; #endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY -static struct pbl_flash_sf32lb52_mpi_state s_flash_state = { - .cfg = { - .Instance = FLASH2, - .line = HAL_FLASH_QMODE, - .base = FLASH2_BASE_ADDR, - .msize = 16, - .SpiMode = SPI_MODE_NOR, - }, - .dma = { - .Instance = DMA1_Channel2, - .dma_irq = DMAC1_CH2_IRQn, - .request = DMA_REQUEST_1, - }, -}; -static const struct pbl_flash_sf32lb52_mpi s_flash = { - .dev = - { - .state = &s_flash_state.flash, - .ops = &pbl_flash_sf32lb52_mpi_ops, - .base = FLASH_REGION_BASE_ADDRESS, - .size = BOARD_NOR_FLASH_SIZE, - .sector_size = SECTOR_SIZE_BYTES, - .subsector_size = SUBSECTOR_SIZE_BYTES, - .sec_regs = &pbl_flash_nor_gd25q256e.sec_regs, - }, - .name = "GD25Q256E", - .id = 0x1940c8, - .clk_div = 0U, - .dpd_enter_us = 3, - .dpd_exit_us = 20, -}; -const struct pbl_flash_device *const FLASH = &s_flash.dev; static I2CBusHalState s_i2c_bus_hal_state_1 = { .hdl = { diff --git a/src/fw/board/boards/board_qemu_emery.c b/src/fw/board/boards/board_qemu_emery.c index 490837e21d..28b91a4cb7 100644 --- a/src/fw/board/boards/board_qemu_emery.c +++ b/src/fw/board/boards/board_qemu_emery.c @@ -2,8 +2,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" -#include "flash_region/flash_region.h" -#include // UART device for debug serial #include @@ -35,21 +33,6 @@ static struct UARTDevice QEMU_UART_DEVICE = { UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; -static struct pbl_flash_device_state s_flash_state; -static const struct pbl_flash_qemu s_flash = { - .dev = - { - .state = &s_flash_state, - .ops = &pbl_flash_qemu_ops, - .base = FLASH_REGION_BASE_ADDRESS, - .size = BOARD_NOR_FLASH_SIZE, - .sector_size = SECTOR_SIZE_BYTES, - .subsector_size = SUBSECTOR_SIZE_BYTES, - }, - .regs = QEMU_EXTFLASH_BASE, -}; -const struct pbl_flash_device *const FLASH = &s_flash.dev; - // Display device - QEMU framebuffer at DISPLAY_BASE + 0x1000 static QemuDisplayDevice s_display = { .base_addr = QEMU_DISPLAY_BASE, diff --git a/src/fw/board/boards/board_qemu_flint.c b/src/fw/board/boards/board_qemu_flint.c index 5256fb0d4a..24a1ad8830 100644 --- a/src/fw/board/boards/board_qemu_flint.c +++ b/src/fw/board/boards/board_qemu_flint.c @@ -2,8 +2,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" -#include "flash_region/flash_region.h" -#include // UART device for debug serial #include @@ -33,21 +31,6 @@ static struct UARTDevice QEMU_UART_DEVICE = { UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; -static struct pbl_flash_device_state s_flash_state; -static const struct pbl_flash_qemu s_flash = { - .dev = - { - .state = &s_flash_state, - .ops = &pbl_flash_qemu_ops, - .base = FLASH_REGION_BASE_ADDRESS, - .size = BOARD_NOR_FLASH_SIZE, - .sector_size = SECTOR_SIZE_BYTES, - .subsector_size = SUBSECTOR_SIZE_BYTES, - }, - .regs = QEMU_EXTFLASH_BASE, -}; -const struct pbl_flash_device *const FLASH = &s_flash.dev; - // Display device - QEMU framebuffer static QemuDisplayDevice s_display = { .base_addr = QEMU_DISPLAY_BASE, diff --git a/src/fw/board/boards/board_qemu_gabbro.c b/src/fw/board/boards/board_qemu_gabbro.c index 9c93ce67ca..afcc2c2707 100644 --- a/src/fw/board/boards/board_qemu_gabbro.c +++ b/src/fw/board/boards/board_qemu_gabbro.c @@ -2,8 +2,6 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" -#include "flash_region/flash_region.h" -#include // UART device for debug serial #include @@ -32,21 +30,6 @@ static struct UARTDevice QEMU_UART_DEVICE = { UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; -static struct pbl_flash_device_state s_flash_state; -static const struct pbl_flash_qemu s_flash = { - .dev = - { - .state = &s_flash_state, - .ops = &pbl_flash_qemu_ops, - .base = FLASH_REGION_BASE_ADDRESS, - .size = BOARD_NOR_FLASH_SIZE, - .sector_size = SECTOR_SIZE_BYTES, - .subsector_size = SUBSECTOR_SIZE_BYTES, - }, - .regs = QEMU_EXTFLASH_BASE, -}; -const struct pbl_flash_device *const FLASH = &s_flash.dev; - // Display device - QEMU framebuffer static QemuDisplayDevice s_display = { .base_addr = QEMU_DISPLAY_BASE, diff --git a/src/fw/drivers/flash/Kconfig b/src/fw/drivers/flash/Kconfig index 67349dd01f..24f61e1ae1 100644 --- a/src/fw/drivers/flash/Kconfig +++ b/src/fw/drivers/flash/Kconfig @@ -8,6 +8,21 @@ menuconfig FLASH if FLASH +choice FLASH_NOR_PART + prompt "NOR flash part" + optional + help + The SPI NOR part the board is fitted with. Selects the command set + used by the flash driver and the flash region layout. + +config FLASH_GD25LQ255E + bool "GigaDevice GD25LQ255E" + +config FLASH_GD25Q256E + bool "GigaDevice GD25Q256E" + +endchoice + config FLASH_NRF5_QSPI bool "nRF5 QSPI NOR flash" depends on SOC_NRF52 @@ -15,6 +30,79 @@ config FLASH_NRF5_QSPI help SPI NOR flash attached to the nRF5 QSPI peripheral. +if FLASH_NRF5_QSPI + +config FLASH_NRF5_QSPI_CLK_FREQ_HZ + int "QSPI clock frequency (Hz)" + default 8000000 + help + Frequencies above 8 MHz are exposed to nRF52 anomaly 244. + +config FLASH_NRF5_QSPI_SCK_PIN + int "SCK pin" + default 0 + +config FLASH_NRF5_QSPI_CSN_PIN + int "CSN pin" + default 0 + +config FLASH_NRF5_QSPI_IO0_PIN + int "IO0 pin" + default 0 + +config FLASH_NRF5_QSPI_IO1_PIN + int "IO1 pin" + default 0 + +config FLASH_NRF5_QSPI_IO2_PIN + int "IO2 pin" + default 0 + +config FLASH_NRF5_QSPI_IO3_PIN + int "IO3 pin" + default 0 + +choice FLASH_NRF5_QSPI_READ_MODE + prompt "Read command" + default FLASH_NRF5_QSPI_READ_READ4IO + +config FLASH_NRF5_QSPI_READ_FASTREAD + bool "Fast read (single line)" + +config FLASH_NRF5_QSPI_READ_READ2O + bool "Dual output" + +config FLASH_NRF5_QSPI_READ_READ2IO + bool "Dual I/O" + +config FLASH_NRF5_QSPI_READ_READ4O + bool "Quad output" + +config FLASH_NRF5_QSPI_READ_READ4IO + bool "Quad I/O" + +endchoice + +choice FLASH_NRF5_QSPI_WRITE_MODE + prompt "Program command" + default FLASH_NRF5_QSPI_WRITE_PP4O + +config FLASH_NRF5_QSPI_WRITE_PP + bool "Page program (single line)" + +config FLASH_NRF5_QSPI_WRITE_PP2O + bool "Dual output" + +config FLASH_NRF5_QSPI_WRITE_PP4O + bool "Quad output" + +config FLASH_NRF5_QSPI_WRITE_PP4IO + bool "Quad I/O" + +endchoice + +endif # FLASH_NRF5_QSPI + config FLASH_SF32LB52_MPI bool "SF32LB52 MPI NOR flash" depends on SOC_SF32LB52 @@ -22,6 +110,32 @@ config FLASH_SF32LB52_MPI help SPI NOR flash attached to an SF32LB52 MPI controller. +if FLASH_SF32LB52_MPI + +config FLASH_SF32LB52_MPI_INSTANCE + int "MPI controller instance" + range 1 2 + default 2 + +config FLASH_SF32LB52_MPI_MSIZE + int "Memory window size (MB)" + default 32 + +config FLASH_SF32LB52_MPI_CLK_DIV + int "Clock divider" + default 0 + +config FLASH_SF32LB52_MPI_DMA_CHANNEL + int "DMAC1 channel" + range 1 8 + default 2 + +config FLASH_SF32LB52_MPI_DMA_REQUEST + int "DMA request" + default 1 + +endif # FLASH_SF32LB52_MPI + config FLASH_QEMU bool "QEMU External Flash" depends on SOC_QEMU @@ -29,15 +143,13 @@ config FLASH_QEMU help Support for the QEMU external flash peripheral. -config FLASH_GD25LQ255E - bool "GigaDevice GD25LQ255E" - help - Support for the GigaDevice GD25LQ255E NOR flash. +if FLASH_QEMU -config FLASH_GD25Q256E - bool "GigaDevice GD25Q256E" - help - Support for the GigaDevice GD25Q256E NOR flash. +config FLASH_QEMU_SIZE + hex "Flash size" + default 0x2000000 + +endif # FLASH_QEMU module = DRIVER_FLASH module-str = Flash diff --git a/src/fw/drivers/flash/README.md b/src/fw/drivers/flash/README.md index 16b11269e8..b39daf78c8 100644 --- a/src/fw/drivers/flash/README.md +++ b/src/fw/drivers/flash/README.md @@ -2,10 +2,15 @@ Flash Memory Drivers -------------------- `include/pbl/drivers/flash.h` is the only flash API. It operates on a -`struct pbl_flash_device`, which boards define and export as `FLASH`. The -generic layer (`flash.c`) owns locking, write protection, blank checks and the -erase engine; drivers implement `struct pbl_flash_ops` for a controller and -never use OS services beyond what the generic layer hands them. +`struct pbl_flash_device`; the generic layer (`flash.c`) owns locking, write +protection, blank checks and the erase engine, while drivers implement +`struct pbl_flash_ops` for a controller. + +Devices are instantiated by the drivers themselves, not by boards: each +driver defines its device from Kconfig (`FLASH_NRF5_QSPI_*`, +`FLASH_SF32LB52_MPI_*`, `FLASH_QEMU_*`) and the selected NOR part +(`FLASH_NOR_PART` choice, whose command sets and geometry live in +`nor_part.h`), and exports it as `FLASH`. After `pbl_flash_coredump_init()` the same API works without locks, timers or sleeping so that core dumps can be written from a fault handler. diff --git a/src/fw/drivers/flash/flash.c b/src/fw/drivers/flash/flash.c index 4eeb857cf0..e7c4681413 100644 --- a/src/fw/drivers/flash/flash.c +++ b/src/fw/drivers/flash/flash.c @@ -30,7 +30,8 @@ PBL_LOG_MODULE_DEFINE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); #define ERASE_YIELD_MS 4 static bool prv_in_range(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { - return addr >= dev->base && len <= dev->size && addr - dev->base <= dev->size - len; + return addr >= dev->base && len <= dev->geometry->size && + addr - dev->base <= dev->geometry->size - len; } static bool prv_is_protected(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { @@ -107,10 +108,10 @@ static void prv_schedule_resume(const struct pbl_flash_device *dev, uint32_t del } static uint32_t prv_erase_unit(const struct pbl_flash_device *dev, uint32_t addr, uint32_t end) { - if ((addr & (dev->sector_size - 1)) == 0 && addr + dev->sector_size <= end) { - return dev->sector_size; + if ((addr & (dev->geometry->sector_size - 1)) == 0 && addr + dev->geometry->sector_size <= end) { + return dev->geometry->sector_size; } - return dev->subsector_size; + return dev->geometry->subsector_size; } // Lock held. @@ -182,8 +183,9 @@ static bool prv_erase_step(const struct pbl_flash_device *dev, uint32_t *wait_ms if (dev->ops->erase_status != NULL) { st->erase.in_progress = true; - st->erase.expected_ms = - (unit == dev->sector_size) ? dev->sector_erase_ms : dev->subsector_erase_ms; + st->erase.expected_ms = (unit == dev->geometry->sector_size) + ? dev->geometry->sector_erase_ms + : dev->geometry->subsector_erase_ms; *wait_ms = MAX(1, st->erase.expected_ms * 7 / 8); } else { prv_erase_unit_done(dev); @@ -215,8 +217,8 @@ static void prv_poll_timer_cb(void *ctx) { } static size_t prv_erase_len(const struct pbl_flash_device *dev, uint32_t addr, size_t len) { - PBL_ASSERTN((addr & (dev->subsector_size - 1)) == 0); - len = (len + dev->subsector_size - 1) & ~(size_t)(dev->subsector_size - 1); + PBL_ASSERTN((addr & (dev->geometry->subsector_size - 1)) == 0); + len = (len + dev->geometry->subsector_size - 1) & ~(size_t)(dev->geometry->subsector_size - 1); PBL_ASSERTN(prv_in_range(dev, addr, len)); return len; } diff --git a/src/fw/drivers/flash/gd25lq255e.c b/src/fw/drivers/flash/gd25lq255e.c index 647cd88d5e..11ed1e3a5b 100644 --- a/src/fw/drivers/flash/gd25lq255e.c +++ b/src/fw/drivers/flash/gd25lq255e.c @@ -13,7 +13,15 @@ static const uint32_t s_sec_reg_addrs[] = { const struct pbl_flash_nor_part pbl_flash_nor_gd25lq255e = { .name = "GD25LQ255E", .id = 0x1960c8, - .size = 0x2000000, + .geometry = + { + .size = 0x2000000, + .page_size = 256, + .sector_size = 0x10000, + .subsector_size = 0x1000, + .sector_erase_ms = 150, + .subsector_erase_ms = 50, + }, .cmd = { .write_enable = 0x06, diff --git a/src/fw/drivers/flash/gd25q256e.c b/src/fw/drivers/flash/gd25q256e.c index 733e42340a..c690209fcb 100644 --- a/src/fw/drivers/flash/gd25q256e.c +++ b/src/fw/drivers/flash/gd25q256e.c @@ -14,7 +14,15 @@ static const uint32_t s_sec_reg_addrs[] = { const struct pbl_flash_nor_part pbl_flash_nor_gd25q256e = { .name = "GD25Q256E", .id = 0x1940c8, - .size = 0x2000000, + .geometry = + { + .size = 0x2000000, + .page_size = 256, + .sector_size = 0x10000, + .subsector_size = 0x1000, + .sector_erase_ms = 150, + .subsector_erase_ms = 50, + }, .cmd = { .write_enable = 0x06, diff --git a/src/fw/drivers/flash/nrf5_qspi.c b/src/fw/drivers/flash/nrf5_qspi.c index 0a9a5c1d62..e87eabd3de 100644 --- a/src/fw/drivers/flash/nrf5_qspi.c +++ b/src/fw/drivers/flash/nrf5_qspi.c @@ -1,7 +1,8 @@ /* SPDX-FileCopyrightText: 2026 Core Devices LLC */ /* SPDX-License-Identifier: Apache-2.0 */ -#include +#include +#include #include #include @@ -31,14 +32,44 @@ PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); // switching and semaphore handling, so we define a minimum size for async ops. #define MIN_RW_ASYNC_SIZE 256U -#define PAGE_SIZE 256U - // Bits 15-12 of a security register address are its one-based index. #define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) // Minimum size to enable 4-byte addressing #define ADDR_4BYTE_THRESHOLD 0x1000000UL +enum read_mode { + READ_FASTREAD, + READ_READ2O, + READ_READ2IO, + READ_READ4O, + READ_READ4IO, +}; + +enum write_mode { + WRITE_PP, + WRITE_PP2O, + WRITE_PP4O, + WRITE_PP4IO, +}; + +struct pbl_flash_nrf5_qspi_state { + struct pbl_flash_device_state flash; + struct pbl_sem sem; + bool initialized; +}; + +struct pbl_flash_nrf5_qspi { + struct pbl_flash_device dev; + const struct pbl_flash_nor_part *part; + uint32_t clk_freq_hz; + uint32_t cs_gpio; + uint32_t clk_gpio; + uint32_t data_gpio[4]; + enum read_mode read_mode; + enum write_mode write_mode; +}; + static uint8_t __attribute__((aligned(4))) s_bounce_buf[32]; static struct pbl_flash_nrf5_qspi_state *s_state; @@ -182,11 +213,9 @@ static void prv_configure_qe(const struct pbl_flash_nrf5_qspi *cfg) { const struct pbl_flash_nor_part *part = cfg->part; uint8_t sr[2]; - if (!(cfg->read_mode == PBL_FLASH_NRF5_QSPI_READ_READ2IO || - cfg->read_mode == PBL_FLASH_NRF5_QSPI_READ_READ4O || - cfg->read_mode == PBL_FLASH_NRF5_QSPI_READ_READ4IO || - cfg->write_mode == PBL_FLASH_NRF5_QSPI_WRITE_PP4O || - cfg->write_mode == PBL_FLASH_NRF5_QSPI_WRITE_PP4IO)) { + if (!(cfg->read_mode == READ_READ2IO || cfg->read_mode == READ_READ4O || + cfg->read_mode == READ_READ4IO || cfg->write_mode == WRITE_PP4O || + cfg->write_mode == WRITE_PP4IO)) { return; } @@ -232,7 +261,7 @@ static size_t prv_sec_reg_payload(const struct pbl_flash_nor_part *part, uint32_ uint8_t *out) { size_t len = 0; - if (part->size > ADDR_4BYTE_THRESHOLD) { + if (part->geometry.size > ADDR_4BYTE_THRESHOLD) { out[len++] = (addr >> 24U) & 0xFFU; } out[len++] = (addr >> 16U) & 0xFFU; @@ -272,16 +301,16 @@ static int prv_init(const struct pbl_flash_device *dev) { nrf_qspi_pins_set(NRF_QSPI, &conf_pins); switch (cfg->read_mode) { - case PBL_FLASH_NRF5_QSPI_READ_READ2O: + case READ_READ2O: conf_prot.readoc = NRF_QSPI_READOC_READ2O; break; - case PBL_FLASH_NRF5_QSPI_READ_READ2IO: + case READ_READ2IO: conf_prot.readoc = NRF_QSPI_READOC_READ2IO; break; - case PBL_FLASH_NRF5_QSPI_READ_READ4O: + case READ_READ4O: conf_prot.readoc = NRF_QSPI_READOC_READ4O; break; - case PBL_FLASH_NRF5_QSPI_READ_READ4IO: + case READ_READ4IO: conf_prot.readoc = NRF_QSPI_READOC_READ4IO; break; default: @@ -290,13 +319,13 @@ static int prv_init(const struct pbl_flash_device *dev) { } switch (cfg->write_mode) { - case PBL_FLASH_NRF5_QSPI_WRITE_PP2O: + case WRITE_PP2O: conf_prot.writeoc = NRF_QSPI_WRITEOC_PP2O; break; - case PBL_FLASH_NRF5_QSPI_WRITE_PP4O: + case WRITE_PP4O: conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4O; break; - case PBL_FLASH_NRF5_QSPI_WRITE_PP4IO: + case WRITE_PP4IO: conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4IO; break; default: @@ -304,7 +333,7 @@ static int prv_init(const struct pbl_flash_device *dev) { break; } - if (part->size > ADDR_4BYTE_THRESHOLD) { + if (part->geometry.size > ADDR_4BYTE_THRESHOLD) { conf_prot.addrmode = NRF_QSPI_ADDRMODE_32BIT; } else { conf_prot.addrmode = NRF_QSPI_ADDRMODE_24BIT; @@ -419,10 +448,11 @@ static void prv_write_page(const struct pbl_flash_device *dev, uint32_t addr, co static int prv_write_op(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, size_t len) { + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; const uint8_t *src = buf; while (len > 0) { - size_t chunk = MIN(len, PAGE_SIZE - (addr % PAGE_SIZE)); + size_t chunk = MIN(len, part->geometry.page_size - (addr % part->geometry.page_size)); const uint8_t *data = src; // The peripheral can only read from RAM @@ -445,9 +475,9 @@ static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, si const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; nrf_qspi_erase_len_t len; - if (size == 4096U) { + if (size == part->geometry.subsector_size) { len = NRF_QSPI_ERASE_LEN_4KB; - } else if (size == 65536U) { + } else if (size == part->geometry.sector_size) { len = NRF_QSPI_ERASE_LEN_64KB; } else { return -EINVAL; @@ -632,7 +662,7 @@ static int prv_sec_reg_lock(const struct pbl_flash_device *dev, uint32_t addr) { } #endif -const struct pbl_flash_ops pbl_flash_nrf5_qspi_ops = { +static const struct pbl_flash_ops s_ops = { .init = prv_init, .read = prv_read_op, .write = prv_write_op, @@ -650,3 +680,50 @@ const struct pbl_flash_ops pbl_flash_nrf5_qspi_ops = { .sec_reg_lock = prv_sec_reg_lock, #endif }; + +#if defined(CONFIG_FLASH_NRF5_QSPI_READ_FASTREAD) +#define READ_MODE READ_FASTREAD +#elif defined(CONFIG_FLASH_NRF5_QSPI_READ_READ2O) +#define READ_MODE READ_READ2O +#elif defined(CONFIG_FLASH_NRF5_QSPI_READ_READ2IO) +#define READ_MODE READ_READ2IO +#elif defined(CONFIG_FLASH_NRF5_QSPI_READ_READ4O) +#define READ_MODE READ_READ4O +#else +#define READ_MODE READ_READ4IO +#endif + +#if defined(CONFIG_FLASH_NRF5_QSPI_WRITE_PP) +#define WRITE_MODE WRITE_PP +#elif defined(CONFIG_FLASH_NRF5_QSPI_WRITE_PP2O) +#define WRITE_MODE WRITE_PP2O +#elif defined(CONFIG_FLASH_NRF5_QSPI_WRITE_PP4IO) +#define WRITE_MODE WRITE_PP4IO +#else +#define WRITE_MODE WRITE_PP4O +#endif + +static struct pbl_flash_nrf5_qspi_state s_flash_state; +static const struct pbl_flash_nrf5_qspi s_flash = { + .dev = + { + .state = &s_flash_state.flash, + .ops = &s_ops, + .geometry = &PBL_FLASH_NOR_PART.geometry, + .sec_regs = &PBL_FLASH_NOR_PART.sec_regs, + }, + .part = &PBL_FLASH_NOR_PART, + .clk_freq_hz = CONFIG_FLASH_NRF5_QSPI_CLK_FREQ_HZ, + .cs_gpio = CONFIG_FLASH_NRF5_QSPI_CSN_PIN, + .clk_gpio = CONFIG_FLASH_NRF5_QSPI_SCK_PIN, + .data_gpio = + { + CONFIG_FLASH_NRF5_QSPI_IO0_PIN, + CONFIG_FLASH_NRF5_QSPI_IO1_PIN, + CONFIG_FLASH_NRF5_QSPI_IO2_PIN, + CONFIG_FLASH_NRF5_QSPI_IO3_PIN, + }, + .read_mode = READ_MODE, + .write_mode = WRITE_MODE, +}; +const struct pbl_flash_device *const FLASH = &s_flash.dev; diff --git a/src/fw/drivers/flash/qemu.c b/src/fw/drivers/flash/qemu.c index 1180d04f45..87c82b49f4 100644 --- a/src/fw/drivers/flash/qemu.c +++ b/src/fw/drivers/flash/qemu.c @@ -1,11 +1,12 @@ /* SPDX-FileCopyrightText: 2026 Core Devices LLC */ /* SPDX-License-Identifier: Apache-2.0 */ -#include +#include #include #include +#include "board/board.h" #include "pbl/util/misc.h" #define REG32(base, off) (*(volatile uint32_t *)((base) + (off))) @@ -28,6 +29,12 @@ #define SUBSECTOR_SIZE 4096U #define SECTOR_SIZE 65536U +struct pbl_flash_qemu { + struct pbl_flash_device dev; + //! Controller registers of the QEMU pebble-extflash device. + uintptr_t regs; +}; + static inline uintptr_t prv_regs(const struct pbl_flash_device *dev) { return container_of(dev, const struct pbl_flash_qemu, dev)->regs; } @@ -88,9 +95,30 @@ static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, si return 0; } -const struct pbl_flash_ops pbl_flash_qemu_ops = { +static const struct pbl_flash_ops s_ops = { .init = prv_init, .read = prv_read, .write = prv_write, .erase_begin = prv_erase_begin, }; + +static const struct pbl_flash_geometry s_geometry = { + .size = CONFIG_FLASH_QEMU_SIZE, + .page_size = PAGE_SIZE, + .sector_size = SECTOR_SIZE, + .subsector_size = SUBSECTOR_SIZE, + .sector_erase_ms = 1, + .subsector_erase_ms = 1, +}; +static struct pbl_flash_device_state s_flash_state; +static const struct pbl_flash_qemu s_flash = { + .dev = + { + .state = &s_flash_state, + .ops = &s_ops, + .base = QEMU_EXTFLASH_XIP_BASE, + .geometry = &s_geometry, + }, + .regs = QEMU_EXTFLASH_BASE, +}; +const struct pbl_flash_device *const FLASH = &s_flash.dev; diff --git a/src/fw/drivers/flash/sf32lb52_mpi.c b/src/fw/drivers/flash/sf32lb52_mpi.c index ef725d506a..98334f81e2 100644 --- a/src/fw/drivers/flash/sf32lb52_mpi.c +++ b/src/fw/drivers/flash/sf32lb52_mpi.c @@ -3,6 +3,8 @@ #include +#include + #include #include #include @@ -18,13 +20,35 @@ PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); -#define PAGE_SIZE 256U -#define SUBSECTOR_SIZE 4096U -#define SECTOR_SIZE 65536U - // Bits 15-12 of a security register address are its one-based index. #define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) +#define MPI_INSTANCE_(n) FLASH##n +#define MPI_INSTANCE(n) MPI_INSTANCE_(n) +#define MPI_BASE_ADDR_(n) FLASH##n##_BASE_ADDR +#define MPI_BASE_ADDR(n) MPI_BASE_ADDR_(n) +#define DMA_CHANNEL_(n) DMA1_Channel##n +#define DMA_CHANNEL(n) DMA_CHANNEL_(n) +#define DMA_IRQN_(n) DMAC1_CH##n##_IRQn +#define DMA_IRQN(n) DMA_IRQN_(n) +#define DMA_REQUEST_(n) DMA_REQUEST_##n +#define DMA_REQUEST(n) DMA_REQUEST_(n) + +struct pbl_flash_sf32lb52_mpi_state { + struct pbl_flash_device_state flash; + QSPI_FLASH_CTX_T ctx; + DMA_HandleTypeDef hdma; + qspi_configure_t cfg; + struct dma_config dma; + bool initialized; +}; + +struct pbl_flash_sf32lb52_mpi { + struct pbl_flash_device dev; + const struct pbl_flash_nor_part *part; + uint16_t clk_div; +}; + static inline const struct pbl_flash_sf32lb52_mpi *prv_cfg(const struct pbl_flash_device *dev) { return container_of(dev, const struct pbl_flash_sf32lb52_mpi, dev); } @@ -52,8 +76,8 @@ static int prv_init(const struct pbl_flash_device *dev) { res = HAL_FLASH_Init(&state->ctx, &state->cfg, &state->hdma, &state->dma, cfg->clk_div); PBL_ASSERT(res == HAL_OK, "HAL_FLASH_Init failed"); - if (state->ctx.dev_id != cfg->id) { - PBL_LOG_ERR("Flash is not %s (id: 0x%" PRIx32 ")", cfg->name, state->ctx.dev_id); + if (state->ctx.dev_id != cfg->part->id) { + PBL_LOG_ERR("Flash is not %s (id: 0x%" PRIx32 ")", cfg->part->name, state->ctx.dev_id); } state->initialized = true; @@ -69,6 +93,7 @@ static int prv_read(const struct pbl_flash_device *dev, uint32_t addr, void *buf static int prv_write(const struct pbl_flash_device *dev, uint32_t addr, const void *buf, size_t len) { FLASH_HandleTypeDef *hflash = prv_handle(dev); + uint32_t page_size = prv_cfg(dev)->part->geometry.page_size; uint8_t *local_buf = NULL; const uint8_t *src = buf; int ret = 0; @@ -95,7 +120,7 @@ static int prv_write(const struct pbl_flash_device *dev, uint32_t addr, const vo size_t remain = len; while (remain > 0) { - size_t chunk = PAGE_SIZE - (offset % PAGE_SIZE); + size_t chunk = page_size - (offset % page_size); if (chunk > remain) { chunk = remain; } @@ -125,6 +150,7 @@ static int prv_write(const struct pbl_flash_device *dev, uint32_t addr, const vo static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, size_t size) { FLASH_HandleTypeDef *hflash = prv_handle(dev); + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; int res; if (addr < hflash->base || addr > hflash->base + hflash->size) { @@ -134,9 +160,9 @@ static int prv_erase_begin(const struct pbl_flash_device *dev, uint32_t addr, si uint32_t offset = addr - hflash->base; pbl_irq_lock(); - if (size == SECTOR_SIZE) { + if (size == part->geometry.sector_size) { res = HAL_QSPIEX_BLK64_ERASE(hflash, offset); - } else if (size == SUBSECTOR_SIZE) { + } else if (size == part->geometry.subsector_size) { res = HAL_QSPIEX_SECT_ERASE(hflash, offset); } else { res = -1; @@ -158,12 +184,12 @@ void pbl_flash_sf32lb52_mpi_dpd_enter(const struct pbl_flash_device *dev) { HAL_FLASH_NOP_CMD(hflash); HAL_FLASH_DEEP_PWRDOWN(hflash); - HAL_Delay_us(prv_cfg(dev)->dpd_enter_us); + HAL_Delay_us(prv_cfg(dev)->part->standby_to_low_power_latency_us); } void pbl_flash_sf32lb52_mpi_dpd_exit(const struct pbl_flash_device *dev) { HAL_FLASH_RELEASE_DPD(prv_handle(dev)); - HAL_Delay_us(prv_cfg(dev)->dpd_exit_us); + HAL_Delay_us(prv_cfg(dev)->part->low_power_to_standby_latency_us); } static int prv_sec_reg_check(const struct pbl_flash_device *dev, uint32_t addr) { @@ -268,7 +294,7 @@ static int prv_sec_reg_lock(const struct pbl_flash_device *dev, uint32_t addr) { } #endif -const struct pbl_flash_ops pbl_flash_sf32lb52_mpi_ops = { +static const struct pbl_flash_ops s_ops = { .init = prv_init, .read = prv_read, .write = prv_write, @@ -282,3 +308,33 @@ const struct pbl_flash_ops pbl_flash_sf32lb52_mpi_ops = { .sec_reg_lock = prv_sec_reg_lock, #endif }; + +static struct pbl_flash_sf32lb52_mpi_state s_flash_state = { + .cfg = + { + .Instance = MPI_INSTANCE(CONFIG_FLASH_SF32LB52_MPI_INSTANCE), + .line = HAL_FLASH_QMODE, + .base = MPI_BASE_ADDR(CONFIG_FLASH_SF32LB52_MPI_INSTANCE), + .msize = CONFIG_FLASH_SF32LB52_MPI_MSIZE, + .SpiMode = SPI_MODE_NOR, + }, + .dma = + { + .Instance = DMA_CHANNEL(CONFIG_FLASH_SF32LB52_MPI_DMA_CHANNEL), + .dma_irq = DMA_IRQN(CONFIG_FLASH_SF32LB52_MPI_DMA_CHANNEL), + .request = DMA_REQUEST(CONFIG_FLASH_SF32LB52_MPI_DMA_REQUEST), + }, +}; +static const struct pbl_flash_sf32lb52_mpi s_flash = { + .dev = + { + .state = &s_flash_state.flash, + .ops = &s_ops, + .base = MPI_BASE_ADDR(CONFIG_FLASH_SF32LB52_MPI_INSTANCE), + .geometry = &PBL_FLASH_NOR_PART.geometry, + .sec_regs = &PBL_FLASH_NOR_PART.sec_regs, + }, + .part = &PBL_FLASH_NOR_PART, + .clk_div = CONFIG_FLASH_SF32LB52_MPI_CLK_DIV, +}; +const struct pbl_flash_device *const FLASH = &s_flash.dev; diff --git a/tests/fakes/fake_spi_flash.c b/tests/fakes/fake_spi_flash.c index 415cfaa40c..1926bf52ba 100644 --- a/tests/fakes/fake_spi_flash.c +++ b/tests/fakes/fake_spi_flash.c @@ -209,13 +209,17 @@ int pbl_flash_init(const struct pbl_flash_device *dev) { return 0; } void pbl_flash_stop(const struct pbl_flash_device *dev) {} +static const struct pbl_flash_geometry s_fake_geometry = { + .size = UINT32_MAX, + .page_size = 256, + .sector_size = SECTOR_SIZE_BYTES, + .subsector_size = SUBSECTOR_SIZE_BYTES, +}; static struct pbl_flash_device_state s_fake_device_state; static const struct pbl_flash_device s_fake_device = { .state = &s_fake_device_state, .base = 0, - .size = UINT32_MAX, - .sector_size = SECTOR_SIZE_BYTES, - .subsector_size = SUBSECTOR_SIZE_BYTES, + .geometry = &s_fake_geometry, }; const struct pbl_flash_device *const FLASH = &s_fake_device; diff --git a/tests/fw/drivers/test_flash_api.c b/tests/fw/drivers/test_flash_api.c index bf861a7785..98f4b308f6 100644 --- a/tests/fw/drivers/test_flash_api.c +++ b/tests/fw/drivers/test_flash_api.c @@ -98,17 +98,21 @@ static const struct pbl_flash_ops s_async_ops = { .erase_resume = prv_erase_resume, }; -static struct pbl_flash_device_state s_state; -static struct pbl_flash_device s_dev = { - .state = &s_state, - .ops = &s_sync_ops, - .base = 0, +static const struct pbl_flash_geometry s_geometry = { .size = 0x100000, + .page_size = 256, .sector_size = SECTOR_SIZE, .subsector_size = SUBSECTOR_SIZE, .sector_erase_ms = 100, .subsector_erase_ms = 100, }; +static struct pbl_flash_device_state s_state; +static struct pbl_flash_device s_dev = { + .state = &s_state, + .ops = &s_sync_ops, + .base = 0, + .geometry = &s_geometry, +}; static void *s_cb_ctx; static int s_cb_status;