mirror of
https://github.com/google/pebble.git
synced 2025-07-04 22:00:38 -04:00
322 lines
13 KiB
C
322 lines
13 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#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.
|
|
//!
|
|
//! This function configures microcontroller peripherals. It should be guarded
|
|
//! with periph_config_acquire_lock/periph_config_release_lock.
|
|
//!
|
|
//! @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: portEND_SWITCHING_ISR is 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);
|
|
|
|
//! Save the address of an erase in progress to a nonvolatile location. The
|
|
//! erase address, along with the fact that an erase is in progress, must be
|
|
//! able to survive a system crash and reboot.
|
|
//!
|
|
//! @note Writing this data to the same flash array that is being erased is
|
|
//! almost certainly a bad idea.
|
|
//!
|
|
//! @param is_subsector True if the erase is a subsector.
|
|
//!
|
|
//! @param addr The address being erased.
|
|
//!
|
|
//! @return S_SUCCESS if the data was successfully stored.
|
|
status_t flash_impl_set_nvram_erase_status(bool is_subsector,
|
|
FlashAddress addr);
|
|
|
|
//! Save to a nonvolatile location the fact that no erase is in progress.
|
|
//!
|
|
//! @return S_SUCCESS if the status was successfully stored.
|
|
status_t flash_impl_clear_nvram_erase_status(void);
|
|
|
|
//! Retrieve the erase status previously set by
|
|
//! flash_impl_set_nvram_erase_status or flash_impl_clear_nvram_erase_status.
|
|
//!
|
|
//! @param [out] is_subsector The value of is_subsector passed to the most
|
|
//! most recent call to flash_impl_set_nvram_erase_status if the status was
|
|
//! not subsequently cleared by flash_impl_clear_nvram_erase_status. The
|
|
//! pointer should not be written to if the erase status was cleared.
|
|
//!
|
|
//! @param [out] addr The address passed to the most recent call to
|
|
//! flash_impl_set_nvram_erase_status if the status was not subsequently
|
|
//! cleared by flash_impl_clear_nvram_erase_status. The address should not be
|
|
//! written to if the erase status was cleared.
|
|
//!
|
|
//! @return S_TRUE if an erase was in progress; S_FALSE otherwise.
|
|
status_t flash_impl_get_nvram_erase_status(bool *is_subsector,
|
|
FlashAddress *addr);
|
|
|
|
void flash_impl_use(void);
|
|
void flash_impl_release(void);
|
|
void flash_impl_release_many(uint32_t num_locks);
|