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/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 073e09f244..7231c14b67 100644 --- a/include/pbl/drivers/flash.h +++ b/include/pbl/drivers/flash.h @@ -1,257 +1,146 @@ -/* 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" - -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; - 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); - -/** - * 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 - * 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 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. - * - * 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); - -/** - * 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 -// 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); - -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); - -//! 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); - -//! 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. -//! -//! @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); + +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; + 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; + const struct pbl_flash_geometry *geometry; + //! 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..eb919fddd3 --- /dev/null +++ b/include/pbl/drivers/flash/nor_part.h @@ -0,0 +1,65 @@ +/* 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; + struct pbl_flash_geometry geometry; + 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; + +//! 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/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; // + +//! 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..f4996407db 100644 --- a/src/fw/board/boards/board_asterix.c +++ b/src/fw/board/boards/board_asterix.c @@ -5,7 +5,6 @@ #include "board/board.h" #include -#include #include #include #include @@ -14,41 +13,14 @@ #include #include #include -#include #include -#include "flash_region/flash_region.h" -// QSPI #include #include #include #include #include -static QSPIPortState s_qspi_port_state; -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, - .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), - }, -}; -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; /* 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..e33b02d6f4 100644 --- a/src/fw/board/boards/board_getafix.c +++ b/src/fw/board/boards/board_getafix.c @@ -75,35 +75,6 @@ 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 = { - .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, - }, - .t_enter_deep_us = 3, - .t_exit_deep_us = 20, -}; - -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, - .clk_div = 0U, -}; -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; 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..c4fe3cf99f 100644 --- a/src/fw/board/boards/board_obelix.c +++ b/src/fw/board/boards/board_obelix.c @@ -194,35 +194,6 @@ 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 = { - .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, - }, - .t_enter_deep_us = 3, - .t_exit_deep_us = 20, -}; - -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, - .clk_div = 0U, -}; -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; 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/console/prompt_commands.c b/src/fw/console/prompt_commands.c index ddcf6fbb0e..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" @@ -72,10 +74,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) { @@ -93,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"); } @@ -122,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); @@ -148,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); } @@ -182,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++) { @@ -196,11 +190,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); @@ -235,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; @@ -249,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; } @@ -263,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]; @@ -290,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) { @@ -307,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; } @@ -319,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; @@ -373,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; } @@ -387,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; } @@ -400,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; } @@ -410,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; } @@ -425,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); } } @@ -453,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"); @@ -502,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); @@ -521,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; @@ -580,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 */ @@ -922,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/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/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..24f61e1ae1 100644 --- a/src/fw/drivers/flash/Kconfig +++ b/src/fw/drivers/flash/Kconfig @@ -8,22 +8,149 @@ 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" - help - Support for the GigaDevice GD25LQ255E NOR flash. config FLASH_GD25Q256E bool "GigaDevice GD25Q256E" + +endchoice + +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. + +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 + default y help - Support for the GigaDevice GD25Q256E NOR flash. + 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" - default y if SOC_QEMU + depends on SOC_QEMU + default y help Support for the QEMU external flash peripheral. +if FLASH_QEMU + +config FLASH_QEMU_SIZE + hex "Flash size" + default 0x2000000 + +endif # FLASH_QEMU + 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..b39daf78c8 100644 --- a/src/fw/drivers/flash/README.md +++ b/src/fw/drivers/flash/README.md @@ -1,16 +1,16 @@ 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`; 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. -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. +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/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..e7c4681413 --- /dev/null +++ b/src/fw/drivers/flash/flash.c @@ -0,0 +1,526 @@ +/* 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->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) { + 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->geometry->sector_size - 1)) == 0 && addr + dev->geometry->sector_size <= end) { + return dev->geometry->sector_size; + } + return dev->geometry->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->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); + } + +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->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; +} + +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 b260870ad3..0000000000 --- a/src/fw/drivers/flash/flash_api.c +++ /dev/null @@ -1,532 +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_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); - 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); -} - -#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) { - return s_flash_initialized; -} - -void flash_stop(void) { - if (!flash_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); - } - } -} - -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); -} - -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)); -} - -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; - - 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..11ed1e3a5b 100644 --- a/src/fw/drivers/flash/gd25lq255e.c +++ b/src/fw/drivers/flash/gd25lq255e.c @@ -1,38 +1,30 @@ -/* 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, + .geometry = + { + .size = 0x2000000, + .page_size = 256, + .sector_size = 0x10000, + .subsector_size = 0x1000, + .sector_erase_ms = 150, + .subsector_erase_ms = 50, + }, + .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 +32,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 = + .mask = { - .sec_lock = (1 << 5) | (1 << 4), /* SR2, page 12 */ - .erase_suspend = 1 << 7, /* SR2 SUS1, page 14 */ + .sr1_busy = 1 << 0, + .sr2_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 = 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..c690209fcb 100644 --- a/src/fw/drivers/flash/gd25q256e.c +++ b/src/fw/drivers/flash/gd25q256e.c @@ -1,39 +1,31 @@ -/* 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, + .geometry = + { + .size = 0x2000000, + .page_size = 256, + .sector_size = 0x10000, + .subsector_size = 0x1000, + .sector_erase_ms = 150, + .subsector_erase_ms = 50, + }, + .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 +35,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 = + .mask = { - .sec_lock = (1 << 5) | (1 << 4) | (1 << 3), /* SR2, page 12 */ - .erase_suspend = 1 << 7, /* SR2 SUS1, page 14 */ + .sr1_busy = 1 << 0, + .sr2_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 = 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..e87eabd3de --- /dev/null +++ b/src/fw/drivers/flash/nrf5_qspi.c @@ -0,0 +1,729 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#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 + +// 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; + +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 == READ_READ2IO || cfg->read_mode == READ_READ4O || + cfg->read_mode == READ_READ4IO || cfg->write_mode == WRITE_PP4O || + cfg->write_mode == 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->geometry.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 READ_READ2O: + conf_prot.readoc = NRF_QSPI_READOC_READ2O; + break; + case READ_READ2IO: + conf_prot.readoc = NRF_QSPI_READOC_READ2IO; + break; + case READ_READ4O: + conf_prot.readoc = NRF_QSPI_READOC_READ4O; + break; + case READ_READ4IO: + conf_prot.readoc = NRF_QSPI_READOC_READ4IO; + break; + default: + conf_prot.readoc = NRF_QSPI_READOC_FASTREAD; + break; + } + + switch (cfg->write_mode) { + case WRITE_PP2O: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP2O; + break; + case WRITE_PP4O: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4O; + break; + case WRITE_PP4IO: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4IO; + break; + default: + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP; + break; + } + + if (part->geometry.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 struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + const uint8_t *src = buf; + + while (len > 0) { + 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 + 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 == part->geometry.subsector_size) { + len = NRF_QSPI_ERASE_LEN_4KB; + } else if (size == part->geometry.sector_size) { + 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 + +static const struct pbl_flash_ops s_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 +}; + +#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/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/drivers/flash/qemu.c b/src/fw/drivers/flash/qemu.c new file mode 100644 index 0000000000..87c82b49f4 --- /dev/null +++ b/src/fw/drivers/flash/qemu.c @@ -0,0 +1,124 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include +#include + +#include "board/board.h" +#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 + +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; +} + +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; +} + +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 new file mode 100644 index 0000000000..98334f81e2 --- /dev/null +++ b/src/fw/drivers/flash/sf32lb52_mpi.c @@ -0,0 +1,340 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#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); + +// 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); +} + +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->part->id) { + PBL_LOG_ERR("Flash is not %s (id: 0x%" PRIx32 ")", cfg->part->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); + uint32_t page_size = prv_cfg(dev)->part->geometry.page_size; + 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); + const struct pbl_flash_nor_part *part = prv_cfg(dev)->part; + int res; + + if (addr < hflash->base || addr > hflash->base + hflash->size) { + return -EINVAL; + } + + uint32_t offset = addr - hflash->base; + + pbl_irq_lock(); + if (size == part->geometry.sector_size) { + res = HAL_QSPIEX_BLK64_ERASE(hflash, offset); + } else if (size == part->geometry.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)->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)->part->low_power_to_standby_latency_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 + +static const struct pbl_flash_ops s_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 +}; + +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/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/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.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/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/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 cbb092c84f..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,10 +174,9 @@ static void init_drivers(void) { pmic_init(); #endif - flash_init(); - flash_sleep_when_idle(true); - flash_enable_write_protection(); - 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 e0fc76392b..91e9aea3cc 100644 --- a/src/fw/services/prf_update/service.c +++ b/src/fw/services/prf_update/service.c @@ -14,10 +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); - - bool saved_sleep_when_idle = flash_get_sleep_when_idle(); - flash_sleep_when_idle(false); + pbl_flash_unprotect(FLASH); #ifndef CONFIG_PBLBOOT FirmwareDescription description = @@ -43,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]; @@ -54,15 +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); - flash_sleep_when_idle(saved_sleep_when_idle); + 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..1926bf52ba 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,76 @@ 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 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, + .geometry = &s_fake_geometry, +}; +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..98f4b308f6 100644 --- a/tests/fw/drivers/test_flash_api.c +++ b/tests/fw/drivers/test_flash_api.c @@ -1,252 +1,325 @@ -/* 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 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; +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);