Merge branch 'master' into feature/esp32s2beta_update

This commit is contained in:
Angus Gratton
2019-08-08 13:44:24 +10:00
committed by Angus Gratton
2414 changed files with 160787 additions and 45783 deletions

View File

@@ -1,23 +1,33 @@
set(priv_requires bootloader_support soc)
if(BOOTLOADER_BUILD)
if (CONFIG_IDF_TARGET_ESP32)
# ESP32 Bootloader needs SPIUnlock from this file, but doesn't
# need other parts of this component
set(COMPONENT_SRCS "spi_flash_rom_patch.c")
set(COMPONENT_PRIV_REQUIRES bootloader_support soc)
# on ESP32, we will include spi_flash_rom_patch.c
# but on other platforms no source files are needed for bootloader
set(srcs)
else()
set(srcs
"cache_utils.c"
"flash_mmap.c"
"flash_ops.c"
"partition.c"
"spi_flash_chip_drivers.c"
"spi_flash_chip_generic.c"
"spi_flash_chip_issi.c"
"spi_flash_os_func_app.c"
"spi_flash_os_func_noos.c"
"memspi_host_driver.c")
if(NOT CONFIG_SPI_FLASH_USE_LEGACY_IMPL)
list(APPEND srcs "esp_flash_api.c" "esp_flash_spi_init.c")
endif()
else() # not BOOTLOADER_BUILD
set(COMPONENT_SRCS "cache_utils.c"
"flash_mmap.c"
"flash_ops.c"
"partition.c"
"spi_flash_rom_patch.c"
"${IDF_TARGET}/flash_ops_${IDF_TARGET}.c")
set(COMPONENT_REQUIRES app_update)
set(COMPONENT_PRIV_REQUIRES bootloader_support soc)
set(priv_requires bootloader_support app_update soc)
endif()
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_PRIV_INCLUDEDIRS ".")
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
if(CONFIG_IDF_TARGET_ESP32)
list(APPEND srcs "spi_flash_rom_patch.c")
endif()
idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES "${priv_requires}"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include
LDFRAGMENTS linker.lf)
register_component()

View File

@@ -52,9 +52,9 @@ menu "SPI Flash driver"
This option is needed to write to flash on ESP32-D2WD, and any configuration
where external SPI flash is connected to non-default pins.
choice SPI_FLASH_WRITING_DANGEROUS_REGIONS
choice SPI_FLASH_DANGEROUS_WRITE
bool "Writing to dangerous flash regions"
default SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
default SPI_FLASH_DANGEROUS_WRITE_ABORTS
help
SPI flash APIs can optionally abort or return a failure code
if erasing or writing addresses that fall at the beginning
@@ -69,14 +69,29 @@ menu "SPI Flash driver"
ROM functions. These functions should not be called directly from IDF
applications.
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
bool "Aborts"
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
bool "Fails"
config SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
bool "Allowed"
config SPI_FLASH_DANGEROUS_WRITE_ABORTS
bool "Aborts"
config SPI_FLASH_DANGEROUS_WRITE_FAILS
bool "Fails"
config SPI_FLASH_DANGEROUS_WRITE_ALLOWED
bool "Allowed"
endchoice
config SPI_FLASH_USE_LEGACY_IMPL
bool "Use the legacy implementation before IDF v4.0"
default n
help
The implementation of SPI flash has been greatly changed in IDF v4.0.
Enable this option to use the legacy implementation.
menu "Auto-detect flash chips"
config SPI_FLASH_SUPPORT_ISSI_CHIP
bool "ISSI"
default y
help
Enable this to support auto detection of ISSI chips if chip vendor not specified.
This adds support for variant chips, however will extend detecting time.
endmenu #auto detect flash chips
endmenu

View File

@@ -1,80 +1,105 @@
SPI Flash APIs
==============
SPI Flash API
=============
Overview
--------
The spi_flash component contains APIs related to reading, writing, erasing,
memory mapping data in the external SPI flash. It also has higher-level
APIs which work with partitions defined in the :doc:`partition table </api-guides/partition-tables>`.
The spi_flash component contains API functions related to reading, writing,
erasing, memory mapping for data in the external flash. The spi_flash
component also has higher-level API functions which work with partitions
defined in the :doc:`partition table </api-guides/partition-tables>`.
Note that all the functionality is limited to the "main" SPI flash chip,
the same SPI flash chip from which program runs. For ``spi_flash_*`` functions,
this is a software limitation. The underlying ROM functions which work with SPI flash
do not have provisions for working with flash chips attached to SPI peripherals
other than SPI0.
Different from the API before IDF v4.0, the functionality is not limited to
the "main" SPI flash chip (the same SPI flash chip from which program runs).
With different chip pointers, you can access to external flashes chips on not
only SPI0/1 but also HSPI/VSPI buses.
SPI flash access APIs
---------------------
.. note::
This is the set of APIs for working with data in flash:
Flash APIs after IDF v4.0 are no longer *atomic*. A writing operation
during another on-going read operation, on the overlapped flash address,
may cause the return data from the read operation to be partly same as
before, and partly updated as new written.
- :cpp:func:`spi_flash_read` used to read data from flash to RAM
- :cpp:func:`spi_flash_write` used to write data from RAM to flash
- :cpp:func:`spi_flash_erase_sector` used to erase individual sectors of flash
- :cpp:func:`spi_flash_erase_range` used to erase range of addresses in flash
- :cpp:func:`spi_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig
Generally, try to avoid using the raw SPI flash functions in favour of
:ref:`partition-specific functions <flash-partition-apis>`.
Kconfig option :ref:`CONFIG_SPI_FLASH_USE_LEGACY_IMPL` can be used to switch
``spi_flash_*`` functions back to the implementation before IDF v4.0.
However, the code size may get bigger if you use the new API and the old API
the same time.
Encrypted reads and writes use the old implementation, even if
:ref:`CONFIG_SPI_FLASH_USE_LEGACY_IMPL` is not enabled. As such, encrypted
flash operations are only supported with the main flash chip (and not with
other flash chips on SPI1 with different CS).
Initializing a flash device
---------------------------
To use ``esp_flash_*`` APIs, you need to have a chip initialized on a certain
SPI bus.
1. Call :cpp:func:`spi_bus_initialize` to properly initialize an SPI bus.
This functions initialize the resources (I/O, DMA, interrupts) shared
among devices attached to this bus.
2. Call :cpp:func:`spi_bus_add_flash_device` to attach the flash device onto
the bus. This allocates memory, and fill the members for the
``esp_flash_t`` structure. The CS I/O is also initialized here.
3. Call :cpp:func:`esp_flash_init` to actually communicate with the chip.
This will also detect the chip type, and influence the following
operations.
.. note:: Multiple flash chips can be attached to the same bus now. However,
using ``esp_flash_*`` devices and ``spi_device_*`` devices on the
same SPI bus is not supported yet.
SPI flash access API
--------------------
This is the set of API functions for working with data in flash:
- :cpp:func:`esp_flash_read` reads data from flash to RAM
- :cpp:func:`esp_flash_write` writes data from RAM to flash
- :cpp:func:`esp_flash_erase_region` erases specific region of flash
- :cpp:func:`esp_flash_erase_chip` erases the whole flash
- :cpp:func:`esp_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig
Generally, try to avoid using the raw SPI flash functions to the "main" SPI
flash chip in favour of :ref:`partition-specific functions
<flash-partition-apis>`.
SPI Flash Size
--------------
The SPI flash size is configured by writing a field in the software bootloader
image header, flashed at offset 0x1000.
The SPI flash size is configured by writing a field in the software bootloader image header, flashed at offset 0x1000.
By default, the SPI flash size is detected by esptool.py when this bootloader is
written to flash, and the header is updated with the correct
size. Alternatively, it is possible to generate a fixed flash size by setting
:envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in ``make menuconfig``.
By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in project configuration.
If it is necessary to override the configured flash size at runtime, is is
possible to set the ``chip_size`` member of ``g_rom_flashchip`` structure. This
size is used by ``spi_flash_*`` functions (in both software & ROM) for bounds
checking.
If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``esp_flash_*`` functions (in both software & ROM) to check the bounds.
Concurrency Constraints
-----------------------
Concurrency Constraints for flash on SPI1
-----------------------------------------
Because the SPI flash is also used for firmware execution (via the instruction &
data caches), these caches must be disabled while reading/writing/erasing. This
means that both CPUs must be running code from IRAM and only reading data from
DRAM while flash write operations occur.
Because the SPI1 flash is also used for firmware execution via the instruction & data caches, these caches must be disabled while reading/writing/erasing. This means that both CPUs must be running code from IRAM and must only be reading data from DRAM while flash write operations occur.
If you use the APIs documented here, then this happens automatically and
transparently. However note that it will have some performance impact on other
tasks in the system.
If you use the API functions documented here, then these constraints are applied automatically and transparently. However, note that it will have some performance impact on other tasks in the system.
Refer to the :ref:`application memory layout <memory-layout>` documentation for
an explanation of the differences between IRAM, DRAM and flash cache.
There are no such constraints and impacts for flash chips on other SPI buses than SPI0/1.
To avoid reading flash cache accidentally, when one CPU commences a flash write
or erase operation the other CPU is put into a blocked state and all
non-IRAM-safe interrupts are disabled on both CPUs, until the flash operation
completes.
For differences between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
To avoid reading flash cache accidentally, when one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state, and all non-IRAM-safe interrupts are disabled on both CPUs until the flash operation completes.
If one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state to avoid reading flash cache accidentally. All interrupts not safe for IRAM are disabled on both CPUs until the flash operation completes.
.. _iram-safe-interrupt-handlers:
IRAM-Safe Interrupt Handlers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you have an interrupt handler that you want to execute even when a flash
operation is in progress (for example, for low latency operations), set the
``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered
</api-reference/system/intr_alloc>`.
If you have an interrupt handler that you want to execute while a flash operation is in progress (for example, for low latency operations), set the ``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered </api-reference/system/intr_alloc>`.
You must ensure all data and functions accessed by these interrupt handlers are
located in IRAM or DRAM. This includes any functions that the handler calls.
You must ensure that all data and functions accessed by these interrupt handlers, including the ones that handlers call, are located in IRAM or DRAM.
Use the ``IRAM_ATTR`` attribute for functions::
@@ -93,80 +118,120 @@ Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data::
const static char *MSG = DRAM_STR("I am a string stored in RAM");
}
Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard,
the compiler will sometimes recognise that a variable or expression is constant
(even if it is not marked ``const``) and optimise it into flash, unless it is
marked with ``DRAM_ATTR``.
Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, the compiler will sometimes recognize that a variable or expression is constant (even if it is not marked ``const``) and optimize it into flash, unless it is marked with ``DRAM_ATTR``.
If a function or symbol is not correctly put into IRAM/DRAM and the interrupt
handler reads from the flash cache during a flash operation, it will cause a
crash due to Illegal Instruction exception (for code which should be in IRAM) or
garbage data to be read (for constant data which should be in DRAM).
If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt handler reads from the flash cache during a flash operation, it will cause a crash due to Illegal Instruction exception (for code which should be in IRAM) or garbage data to be read (for constant data which should be in DRAM).
.. _flash-partition-apis:
Partition table APIs
--------------------
Partition table API
-------------------
ESP-IDF projects use a partition table to maintain information about various regions of
SPI flash memory (bootloader, various application binaries, data, filesystems).
More information about partition tables can be found :doc:`here </api-guides/partition-tables>`.
ESP-IDF projects use a partition table to maintain information about various regions of SPI flash memory (bootloader, various application binaries, data, filesystems). More information on partition tables can be found :doc:`here </api-guides/partition-tables>`.
This component provides APIs to enumerate partitions found in the partition table
and perform operations on them. These functions are declared in ``esp_partition.h``:
This component provides API functions to enumerate partitions found in the partition table and perform operations on them. These functions are declared in ``esp_partition.h``:
- :cpp:func:`esp_partition_find` used to search partition table for entries with
specific type, returns an opaque iterator
- :cpp:func:`esp_partition_get` returns a structure describing the partition, for the given iterator
- :cpp:func:`esp_partition_next` advances iterator to the next partition found
- :cpp:func:`esp_partition_iterator_release` releases iterator returned by ``esp_partition_find``
- :cpp:func:`esp_partition_find_first` is a convenience function which returns structure
describing the first partition found by ``esp_partition_find``
- :cpp:func:`esp_partition_read`, :cpp:func:`esp_partition_write`, :cpp:func:`esp_partition_erase_range`
are equivalent to :cpp:func:`spi_flash_read`, :cpp:func:`spi_flash_write`,
:cpp:func:`spi_flash_erase_range`, but operate within partition boundaries
- :cpp:func:`esp_partition_find` checks a partition table for entries with specific type, returns an opaque iterator.
- :cpp:func:`esp_partition_get` returns a structure describing the partition for a given iterator.
- :cpp:func:`esp_partition_next` shifts the iterator to the next found partition.
- :cpp:func:`esp_partition_iterator_release` releases iterator returned by ``esp_partition_find``.
- :cpp:func:`esp_partition_find_first` - a convenience function which returns the structure describing the first partition found by ``esp_partition_find``.
- :cpp:func:`esp_partition_read`, :cpp:func:`esp_partition_write`, :cpp:func:`esp_partition_erase_range` are equivalent to :cpp:func:`spi_flash_read`, :cpp:func:`spi_flash_write`, :cpp:func:`spi_flash_erase_range`, but operate within partition boundaries.
.. note::
Most application code should use these ``esp_partition_*`` APIs instead of lower level
``spi_flash_*`` APIs. Partition APIs do bounds checking and calculate correct
offsets in flash based on data stored in partition table.
Application code should mostly use these ``esp_partition_*`` API functions instead of lower level ``spi_flash_*`` API functions. Partition table API functions do bounds checking and calculate correct offsets in flash, based on data stored in a partition table.
SPI Flash Encryption
--------------------
It is possible to encrypt SPI flash contents, and have it transparenlty decrypted by hardware.
It is possible to encrypt the contents of SPI flash and have it transparently decrypted by hardware.
Refer to the :doc:`Flash Encryption documentation </security/flash-encryption>` for more details.
Memory mapping APIs
-------------------
Memory mapping API
------------------
ESP32 features memory hardware which allows regions of flash memory to be mapped
into instruction and data address spaces. This mapping works only for read operations,
it is not possible to modify contents of flash memory by writing to mapped memory
region. Mapping happens in 64KB pages. Memory mapping hardware can map up to
4 megabytes of flash into data address space, and up to 16 megabytes of flash into
instruction address space. See the technical reference manual for more details
about memory mapping hardware.
ESP32 features memory hardware which allows regions of flash memory to be mapped into instruction and data address spaces. This mapping works only for read operations. It is not possible to modify contents of flash memory by writing to a mapped memory region.
Note that some number of 64KB pages is used to map the application
itself into memory, so the actual number of available 64KB pages may be less.
Mapping happens in 64KB pages. Memory mapping hardware can map up to four megabytes of flash into data address space and up to 16 megabytes of flash into instruction address space. See the technical reference manual for more details about memory mapping hardware.
Reading data from flash using a memory mapped region is the only way to decrypt
contents of flash when :doc:`flash encryption </security/flash-encryption>` is enabled.
Decryption is performed at hardware level.
Note that some 64KB pages are used to map the application itself into memory, so the actual number of available 64KB pages may be less.
Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``:
Reading data from flash using a memory mapped region is the only way to decrypt contents of flash when :doc:`flash encryption </security/flash-encryption>` is enabled. Decryption is performed at the hardware level.
- :cpp:func:`spi_flash_mmap` maps a region of physical flash addresses into instruction space or data space of the CPU
- :cpp:func:`spi_flash_munmap` unmaps previously mapped region
- :cpp:func:`esp_partition_mmap` maps part of a partition into the instruction space or data space of the CPU
Memory mapping API are declared in ``esp_spi_flash.h`` and ``esp_partition.h``:
- :cpp:func:`spi_flash_mmap` maps a region of physical flash addresses into instruction space or data space of the CPU.
- :cpp:func:`spi_flash_munmap` unmaps previously mapped region.
- :cpp:func:`esp_partition_mmap` maps part of a partition into the instruction space or data space of the CPU.
Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap` are as follows:
- :cpp:func:`spi_flash_mmap` must be given a 64KB aligned physical address
- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition,
it will adjust returned pointer to mapped memory as necessary
- :cpp:func:`spi_flash_mmap` must be given a 64KB aligned physical address.
- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, it will adjust the returned pointer to mapped memory as necessary
Note that because memory mapping happens in 64KB blocks, it may be possible to
read data outside of the partition provided to ``esp_partition_mmap``.
Note that since memory mapping happens in 64KB blocks, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``.
Implementation
--------------
The ``esp_flash_t`` structure holds chip data as well as three important parts of this API:
1. The host driver, which provides the hardware support to access the chip;
2. The chip driver, which provides compatibility service to different chips;
3. The OS functions, provides support of some OS functions (e.g. lock, delay)
in different stages (1st/2st boot, or the app).
Host driver
^^^^^^^^^^^
The host driver relies on an interface (``spi_flash_host_driver_t``) defined
in the ``spi_flash_host_drv.h`` (in the ``soc/include/hal`` folder). This
interface provides some common functions to communicate with the chip.
In other files of the SPI HAL, some of these functions are implemented with
existing ESP32 memory-spi functionalities. However due to the speed
limitations of ESP32, the HAL layer can't provide high-speed implementations
to some reading commands (So we didn't do it at all). The files
(``memspi_host_driver.h`` and ``.c``) implement the high-speed version of
these commands with the ``common_command`` function provided in the HAL, and
wrap these functions as ``spi_flash_host_driver_t`` for upper layer to use.
You can also implement your own host driver, even with the GPIO. As long as
all the functions in the ``spi_flash_host_driver_t`` are implemented, the
esp_flash API can access to the flash regardless of the low-level hardware.
Chip driver
^^^^^^^^^^^
The chip driver, defined in ``spi_flash_chip_driver.h``, wraps basic
functions provided by the host driver for the API layer to use.
Some operations need some commands to be sent first, or read some status
after. Some chips need different command or value, or need special
communication ways.
There is a type of chip called ``generic chip`` which stands for common
chips. Other special chip drivers can be developed on the base of the generic
chip.
The chip driver relies on the host driver.
OS functions
^^^^^^^^^^^^
Currently the OS function layer provides a lock and a delay entries.
The lock is used to resolve the conflicts between the SPI chip access and
other functions. E.g. the cache (used for the code and PSRAM data fetch)
should be disabled when the flash chip on the SPI0/1 is being accessed. Also,
some devices which don't have CS wire, or the wire is controlled by the
software (e.g. SD card via SPI interface), requires the bus to be monopolized
during a period.
The delay is used by some long operations which requires the master to wait
or polling periodically.
The top API wraps these the chip driver and OS functions into an entire
component, and also provides some argument checking.

View File

@@ -0,0 +1,127 @@
SPI Flash API (Legacy)
========================
Overview
--------
This is the readme for the APIs before IDF v4.0. Enable the kconfig option ``SPI_FLASH_USE_LEGACY_IMPL`` to use the
legacy implementation.
The spi_flash component contains API functions related to reading, writing, erasing, memory mapping for data in the external SPI flash. The spi_flash component also has higher-level API functions which work with partitions defined in the :doc:`partition table </api-guides/partition-tables>`.
Note that all the functionality is limited to the "main" SPI flash chip, the same SPI flash chip from which programs are runs. For ``spi_flash_*`` functions, this is a software limitation. The underlying ROM functions which work with SPI flash do not have provisions for working with flash chips attached to SPI peripherals other than SPI0.
SPI flash access API
--------------------
This is the set of API functions for working with data in flash:
- :cpp:func:`spi_flash_read` reads data from flash to RAM
- :cpp:func:`spi_flash_write` writes data from RAM to flash
- :cpp:func:`spi_flash_erase_sector` erases individual sectors of flash
- :cpp:func:`spi_flash_erase_range` erases ranges of addresses in flash
- :cpp:func:`spi_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig
Generally, try to avoid using the raw SPI flash functions in favor of :ref:`partition-specific functions <flash-partition-apis>`.
SPI Flash Size
--------------
The SPI flash size is configured by writing a field in the software bootloader image header, flashed at offset 0x1000.
By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in project configuration.
If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``spi_flash_*`` functions (in both software & ROM) to check the bounds.
Concurrency Constraints
-----------------------
Because the SPI flash is also used for firmware execution via the instruction & data caches, these caches must be disabled while reading/writing/erasing. This means that both CPUs must be running code from IRAM and must only be reading data from DRAM while flash write operations occur.
If you use the API functions documented here, then these constraints are applied automatically and transparently. However, note that it will have some performance impact on other tasks in the system.
For differences between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
To avoid reading flash cache accidentally, when one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state, and all non-IRAM-safe interrupts are disabled on both CPUs until the flash operation completes.
If one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state to avoid reading flash cache accidentally. All interrupts not safe for IRAM are disabled on both CPUs until the flash operation completes.
.. _iram-safe-interrupt-handlers:
IRAM-Safe Interrupt Handlers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you have an interrupt handler that you want to execute while a flash operation is in progress (for example, for low latency operations), set the ``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered </api-reference/system/intr_alloc>`.
You must ensure that all data and functions accessed by these interrupt handlers, including the ones that handlers call, are located in IRAM or DRAM.
Use the ``IRAM_ATTR`` attribute for functions::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data::
void IRAM_ATTR gpio_isr_handler(void* arg)
{
const static DRAM_ATTR uint8_t INDEX_DATA[] = { 45, 33, 12, 0 };
const static char *MSG = DRAM_STR("I am a string stored in RAM");
}
Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, the compiler will sometimes recognize that a variable or expression is constant (even if it is not marked ``const``) and optimize it into flash, unless it is marked with ``DRAM_ATTR``.
If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt handler reads from the flash cache during a flash operation, it will cause a crash due to Illegal Instruction exception (for code which should be in IRAM) or garbage data to be read (for constant data which should be in DRAM).
.. _flash-partition-apis:
Partition table API
-------------------
ESP-IDF projects use a partition table to maintain information about various regions of SPI flash memory (bootloader, various application binaries, data, filesystems). More information on partition tables can be found :doc:`here </api-guides/partition-tables>`.
This component provides API functions to enumerate partitions found in the partition table and perform operations on them. These functions are declared in ``esp_partition.h``:
- :cpp:func:`esp_partition_find` checks a partition table for entries with specific type, returns an opaque iterator.
- :cpp:func:`esp_partition_get` returns a structure describing the partition for a given iterator.
- :cpp:func:`esp_partition_next` shifts the iterator to the next found partition.
- :cpp:func:`esp_partition_iterator_release` releases iterator returned by ``esp_partition_find``.
- :cpp:func:`esp_partition_find_first` - a convenience function which returns the structure describing the first partition found by ``esp_partition_find``.
- :cpp:func:`esp_partition_read`, :cpp:func:`esp_partition_write`, :cpp:func:`esp_partition_erase_range` are equivalent to :cpp:func:`spi_flash_read`, :cpp:func:`spi_flash_write`, :cpp:func:`spi_flash_erase_range`, but operate within partition boundaries.
.. note::
Application code should mostly use these ``esp_partition_*`` API functions instead of lower level ``spi_flash_*`` API functions. Partition table API functions do bounds checking and calculate correct offsets in flash, based on data stored in a partition table.
SPI Flash Encryption
--------------------
It is possible to encrypt the contents of SPI flash and have it transparently decrypted by hardware.
Refer to the :doc:`Flash Encryption documentation </security/flash-encryption>` for more details.
Memory mapping API
------------------
ESP32 features memory hardware which allows regions of flash memory to be mapped into instruction and data address spaces. This mapping works only for read operations. It is not possible to modify contents of flash memory by writing to a mapped memory region.
Mapping happens in 64KB pages. Memory mapping hardware can map up to four megabytes of flash into data address space and up to 16 megabytes of flash into instruction address space. See the technical reference manual for more details about memory mapping hardware.
Note that some 64KB pages are used to map the application itself into memory, so the actual number of available 64KB pages may be less.
Reading data from flash using a memory mapped region is the only way to decrypt contents of flash when :doc:`flash encryption </security/flash-encryption>` is enabled. Decryption is performed at the hardware level.
Memory mapping API are declared in ``esp_spi_flash.h`` and ``esp_partition.h``:
- :cpp:func:`spi_flash_mmap` maps a region of physical flash addresses into instruction space or data space of the CPU.
- :cpp:func:`spi_flash_munmap` unmaps previously mapped region.
- :cpp:func:`esp_partition_mmap` maps part of a partition into the instruction space or data space of the CPU.
Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap` are as follows:
- :cpp:func:`spi_flash_mmap` must be given a 64KB aligned physical address.
- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, it will adjust the returned pointer to mapped memory as necessary
Note that since memory mapping happens in 64KB blocks, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``.

View File

@@ -1,6 +1,8 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := . $(IDF_TARGET)
COMPONENT_PRIV_INCLUDEDIRS := .
COMPONENT_PRIV_INCLUDEDIRS := private_include
COMPONENT_ADD_LDFRAGMENTS += linker.lf
ifdef IS_BOOTLOADER_BUILD

View File

@@ -63,3 +63,74 @@ esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_a
return rc;
}
}
#define SPICACHE SPIMEM0
#define SPIFLASH SPIMEM1
#define FLASH_WRAP_CMD 0x77
esp_err_t spi_flash_wrap_set(spi_flash_wrap_mode_t mode)
{
uint32_t reg_bkp_ctrl = SPIFLASH.ctrl.val;
uint32_t reg_bkp_usr = SPIFLASH.user.val;
SPIFLASH.user.fwrite_dio = 0;
SPIFLASH.user.fwrite_dual = 0;
SPIFLASH.user.fwrite_qio = 1;
SPIFLASH.user.fwrite_quad = 0;
SPIFLASH.ctrl.fcmd_dual = 0;
SPIFLASH.ctrl.fcmd_quad = 0;
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 1;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = FLASH_WRAP_CMD;
SPIFLASH.user1.usr_addr_bitlen = 23;
SPIFLASH.addr = 0;
SPIFLASH.user.usr_miso = 0;
SPIFLASH.user.usr_mosi = 1;
SPIFLASH.mosi_dlen.usr_mosi_bit_len = 7;
SPIFLASH.data_buf[0] = (uint32_t) mode << 4;;
SPIFLASH.cmd.usr = 1;
while(SPIFLASH.cmd.usr != 0)
{ }
SPIFLASH.ctrl.val = reg_bkp_ctrl;
SPIFLASH.user.val = reg_bkp_usr;
return ESP_OK;
}
esp_err_t spi_flash_enable_wrap(uint32_t wrap_size)
{
switch(wrap_size) {
case 8:
return spi_flash_wrap_set(FLASH_WRAP_MODE_8B);
case 16:
return spi_flash_wrap_set(FLASH_WRAP_MODE_16B);
case 32:
return spi_flash_wrap_set(FLASH_WRAP_MODE_32B);
case 64:
return spi_flash_wrap_set(FLASH_WRAP_MODE_64B);
default:
return ESP_FAIL;
}
}
void spi_flash_disable_wrap()
{
spi_flash_wrap_set(FLASH_WRAP_MODE_DISABLE);
}
bool spi_flash_support_wrap_size(uint32_t wrap_size)
{
if (!REG_GET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FREAD_QIO) || !REG_GET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FASTRD_MODE)){
return ESP_FAIL;
}
switch(wrap_size) {
case 0:
case 8:
case 16:
case 32:
case 64:
return true;
default:
return false;
}
}

View File

@@ -0,0 +1,673 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include <stdlib.h>
#include <stdio.h>
#include <sys/param.h>
#include <string.h>
#include "spi_flash_chip_driver.h"
#include "memspi_host_driver.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_heap_caps.h"
static const char TAG[] = "spi_flash";
#define MAX_WRITE_CHUNK 8192 /* write in chunks */
#define MAX_READ_CHUNK 16384
#ifdef CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS
#define UNSAFE_WRITE_ADDRESS abort()
#else
#define UNSAFE_WRITE_ADDRESS return ESP_ERR_INVALID_ARG
#endif
/* CHECK_WRITE_ADDRESS macro to fail writes which land in the
bootloader, partition table, or running application region.
*/
#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
#define CHECK_WRITE_ADDRESS(CHIP, ADDR, SIZE)
#else /* FAILS or ABORTS */
#define CHECK_WRITE_ADDRESS(CHIP, ADDR, SIZE) do { \
if (CHIP && CHIP->host->region_protected && CHIP->host->region_protected(CHIP->host, ADDR, SIZE)) { \
UNSAFE_WRITE_ADDRESS; \
} \
} while(0)
#endif // CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
#define IO_STR_LEN 7
static const char io_mode_str[][IO_STR_LEN] = {
"slowrd",
"fastrd",
"dout",
"dio",
"qout",
"qio",
};
_Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_read_mode_t defined in spi_flash_ll.h");
/* Static function to notify OS of a new SPI flash operation.
If returns an error result, caller must abort. If returns ESP_OK, caller must
call spiflash_end() before returning.
*/
static esp_err_t IRAM_ATTR spiflash_start(esp_flash_t *chip)
{
if (chip->os_func != NULL && chip->os_func->start != NULL) {
esp_err_t err = chip->os_func->start(chip->os_func_data);
if (err != ESP_OK) {
return err;
}
}
chip->host->dev_config(chip->host);
return ESP_OK;
}
/* Static function to notify OS that SPI flash operation is complete.
*/
static esp_err_t IRAM_ATTR spiflash_end(const esp_flash_t *chip, esp_err_t err)
{
if (chip->os_func != NULL
&& chip->os_func->end != NULL) {
esp_err_t end_err = chip->os_func->end(chip->os_func_data);
if (err == ESP_OK) {
err = end_err; // Only return the 'end' error if we haven't already failed
}
}
return err;
}
/* Return true if regions 'a' and 'b' overlap at all, based on their start offsets and lengths. */
inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len);
/* Top-level API functions, calling into chip_drv functions via chip->drv */
static esp_err_t detect_spi_flash_chip(esp_flash_t *chip);
bool esp_flash_chip_driver_initialized(const esp_flash_t *chip)
{
if (!chip->chip_drv) return false;
return true;
}
esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip)
{
esp_err_t err = ESP_OK;
if (chip == NULL || chip->host == NULL || chip->host->driver_data == NULL ||
((memspi_host_data_t*)chip->host->driver_data)->spi == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!esp_flash_chip_driver_initialized(chip)) {
// Detect chip_drv
err = detect_spi_flash_chip(chip);
if (err != ESP_OK) {
return err;
}
}
// Detect flash size
uint32_t size;
err = esp_flash_get_size(chip, &size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to get chip size");
return err;
}
ESP_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]);
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
if (err == ESP_OK) {
// Try to set the flash mode to whatever default mode was chosen
err = chip->chip_drv->set_read_mode(chip);
}
// Done: all fields on 'chip' are initialised
return spiflash_end(chip, err);
}
static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
{
esp_err_t err;
uint32_t flash_id;
int retries = 10;
do {
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
// Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner
// function fails if it sees all-ones or all-zeroes.)
err = chip->host->read_id(chip->host, &flash_id);
if (err == ESP_OK) { // check we see the same ID twice, in case of transient power-on errors
uint32_t new_id;
err = chip->host->read_id(chip->host, &new_id);
if (err == ESP_OK && (new_id != flash_id)) {
err = ESP_ERR_FLASH_NOT_INITIALISED;
}
}
err = spiflash_end(chip, err);
} while (err != ESP_OK && retries-- > 0);
// Detect the chip and set the chip_drv structure for it
const spi_flash_chip_t **drivers = esp_flash_registered_chips;
while (*drivers != NULL && !esp_flash_chip_driver_initialized(chip)) {
chip->chip_drv = *drivers;
// start/end SPI operation each time, for multitasking
// and also so esp_flash_registered_flash_drivers can live in flash
ESP_LOGD(TAG, "trying chip: %s", chip->chip_drv->name);
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
if (chip->chip_drv->probe(chip, flash_id) != ESP_OK) {
chip->chip_drv = NULL;
}
// if probe succeeded, chip->drv stays set
drivers++;
err = spiflash_end(chip, err);
if (err != ESP_OK) {
return err;
}
}
if (!esp_flash_chip_driver_initialized(chip)) {
return ESP_ERR_NOT_FOUND;
}
ESP_LOGI(TAG, "detected chip: %s", chip->chip_drv->name);
return ESP_OK;
}
// Convenience macro for beginning of all API functions,
// check that the 'chip' parameter is properly initialised
// and supports the operation in question
#define VERIFY_OP(OP) do { \
if (chip == NULL) { \
chip = esp_flash_default_chip; \
} \
if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) { \
return ESP_ERR_FLASH_NOT_INITIALISED; \
} \
if (chip->chip_drv->OP == NULL) { \
return ESP_ERR_FLASH_UNSUPPORTED_CHIP; \
} \
} while (0)
esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *out_id)
{
if (chip == NULL) {
chip = esp_flash_default_chip;
}
if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) {
return ESP_ERR_FLASH_NOT_INITIALISED;
}
if (out_id == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->host->read_id(chip->host, out_id);
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size)
{
VERIFY_OP(detect_size);
if (out_size == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (chip->size != 0) {
*out_size = chip->size;
return ESP_OK;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
uint32_t detect_size;
err = chip->chip_drv->detect_size(chip, &detect_size);
if (err == ESP_OK) {
chip->size = detect_size;
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_erase_chip(esp_flash_t *chip)
{
VERIFY_OP(erase_chip);
CHECK_WRITE_ADDRESS(chip, 0, chip->size);
bool write_protect = false;
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = esp_flash_get_chip_write_protect(chip, &write_protect);
if (err == ESP_OK && write_protect) {
err = ESP_ERR_FLASH_PROTECTED;
}
if (err == ESP_OK) {
err = chip->chip_drv->erase_chip(chip);
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len)
{
VERIFY_OP(erase_sector);
VERIFY_OP(erase_block);
CHECK_WRITE_ADDRESS(chip, start, len);
uint32_t block_erase_size = chip->chip_drv->erase_block == NULL ? 0 : chip->chip_drv->block_erase_size;
uint32_t sector_size = chip->chip_drv->sector_size;
bool write_protect = false;
if (sector_size == 0 || (block_erase_size % sector_size) != 0) {
return ESP_ERR_FLASH_NOT_INITIALISED;
}
if (start > chip->size || start + len > chip->size) {
return ESP_ERR_INVALID_ARG;
}
if ((start % chip->chip_drv->sector_size) != 0 || (len % chip->chip_drv->sector_size) != 0) {
// Can only erase multiples of the sector size, starting at sector boundary
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
// Check for write protection on whole chip
if (chip->chip_drv->get_chip_write_protect != NULL) {
err = chip->chip_drv->get_chip_write_protect(chip, &write_protect);
if (err == ESP_OK && write_protect) {
err = ESP_ERR_FLASH_PROTECTED;
}
}
// Check for write protected regions overlapping the erase region
if (err == ESP_OK && chip->chip_drv->get_protected_regions != NULL && chip->chip_drv->num_protectable_regions > 0) {
uint64_t protected = 0;
err = chip->chip_drv->get_protected_regions(chip, &protected);
if (err == ESP_OK && protected != 0) {
for (int i = 0; i < chip->chip_drv->num_protectable_regions && err == ESP_OK; i++) {
const esp_flash_region_t *region = &chip->chip_drv->protectable_regions[i];
if ((protected & BIT64(i))
&& regions_overlap(start, len, region->offset, region->size)) {
err = ESP_ERR_FLASH_PROTECTED;
}
}
}
}
// Don't lock the SPI flash for the entire erase, as this may be very long
err = spiflash_end(chip, err);
while (err == ESP_OK && len >= sector_size) {
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
// If possible erase an entire multi-sector block
if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) {
err = chip->chip_drv->erase_block(chip, start);
start += block_erase_size;
len -= block_erase_size;
}
else {
// Otherwise erase individual sector only
err = chip->chip_drv->erase_sector(chip, start);
start += sector_size;
len -= sector_size;
}
err = spiflash_end(chip, err);
}
return err;
}
esp_err_t IRAM_ATTR esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protected)
{
VERIFY_OP(get_chip_write_protect);
if (write_protected == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_chip_write_protect(chip, write_protected);
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect)
{
VERIFY_OP(set_chip_write_protect);
//TODO: skip writing if already locked or unlocked
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->set_chip_write_protect(chip, write_protect);
return spiflash_end(chip, err);
}
esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **out_regions, uint32_t *out_num_regions)
{
if(out_num_regions != NULL) {
*out_num_regions = 0; // In case caller doesn't check result
}
VERIFY_OP(get_protected_regions);
if(out_regions == NULL || out_num_regions == NULL) {
return ESP_ERR_INVALID_ARG;
}
*out_num_regions = chip->chip_drv->num_protectable_regions;
*out_regions = chip->chip_drv->protectable_regions;
return ESP_OK;
}
static esp_err_t find_region(const esp_flash_t *chip, const esp_flash_region_t *region, uint8_t *index)
{
if (region == NULL) {
return ESP_ERR_INVALID_ARG;
}
for(*index = 0; *index < chip->chip_drv->num_protectable_regions; (*index)++) {
if (memcmp(&chip->chip_drv->protectable_regions[*index],
region, sizeof(esp_flash_region_t)) == 0) {
return ESP_OK;
}
}
return ESP_ERR_NOT_FOUND;
}
esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *out_protected)
{
VERIFY_OP(get_protected_regions);
if (out_protected == NULL) {
return ESP_ERR_INVALID_ARG;
}
uint8_t index;
esp_err_t err = find_region(chip, region, &index);
if (err != ESP_OK) {
return err;
}
uint64_t protection_mask = 0;
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_protected_regions(chip, &protection_mask);
if (err == ESP_OK) {
*out_protected = protection_mask & (1LL << index);
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protect)
{
VERIFY_OP(set_protected_regions);
uint8_t index;
esp_err_t err = find_region(chip, region, &index);
if (err != ESP_OK) {
return err;
}
uint64_t protection_mask = 0;
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_protected_regions(chip, &protection_mask);
if (err == ESP_OK) {
if (protect) {
protection_mask |= (1LL << index);
} else {
protection_mask &= ~(1LL << index);
}
err = chip->chip_drv->set_protected_regions(chip, protection_mask);
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
{
if (length == 0) {
return ESP_OK;
}
VERIFY_OP(read);
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return ESP_ERR_INVALID_ARG;
}
//when the cache is disabled, only the DRAM can be read, check whether we need to receive in another buffer in DRAM.
bool direct_read = chip->host->supports_direct_read(chip->host, buffer);
uint8_t* temp_buffer = NULL;
if (!direct_read) {
uint32_t length_to_allocate = MAX(MAX_READ_CHUNK, length);
length_to_allocate = (length_to_allocate+3)&(~3);
temp_buffer = heap_caps_malloc(length_to_allocate, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_LOGV(TAG, "allocate temp buffer: %p", temp_buffer);
if (temp_buffer == NULL) return ESP_ERR_NO_MEM;
}
esp_err_t err = ESP_OK;
do {
err = spiflash_start(chip);
if (err != ESP_OK) {
break;
}
//if required (dma buffer allocated), read to the buffer instead of the original buffer
uint8_t* buffer_to_read = (temp_buffer)? temp_buffer : buffer;
//each time, we at most read this length
//after that, we release the lock to allow some other operations
uint32_t length_to_read = MIN(MAX_READ_CHUNK, length);
if (err == ESP_OK) {
err = chip->chip_drv->read(chip, buffer_to_read, address, length_to_read);
}
if (err != ESP_OK) {
spiflash_end(chip, err);
break;
}
//even if this is failed, the data is still valid, copy before quit
err = spiflash_end(chip, err);
//copy back to the original buffer
if (temp_buffer) {
memcpy(buffer, temp_buffer, length_to_read);
}
address += length_to_read;
length -= length_to_read;
buffer += length_to_read;
} while (err == ESP_OK && length > 0);
free(temp_buffer);
return err;
}
esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
if (length == 0) {
return ESP_OK;
}
VERIFY_OP(write);
CHECK_WRITE_ADDRESS(chip, address, length);
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return ESP_ERR_INVALID_ARG;
}
//when the cache is disabled, only the DRAM can be read, check whether we need to copy the data first
bool direct_write = chip->host->supports_direct_write(chip->host, buffer);
esp_err_t err = ESP_OK;
/* Write output in chunks, either by buffering on stack or
by artificially cutting into MAX_WRITE_CHUNK parts (in an OS
environment, this prevents writing from causing interrupt or higher priority task
starvation.) */
do {
uint32_t write_len;
const void *write_buf;
if (direct_write) {
write_len = MIN(length, MAX_WRITE_CHUNK);
write_buf = buffer;
} else {
uint32_t buf[8];
write_len = MIN(length, sizeof(buf));
memcpy(buf, buffer, write_len);
write_buf = buf;
}
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->write(chip, write_buf, address, write_len);
address += write_len;
buffer = (void *)((intptr_t)buffer + write_len);
length -= write_len;
err = spiflash_end(chip, err);
} while (err == ESP_OK && length > 0);
return err;
}
esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
VERIFY_OP(write_encrypted);
if (((memspi_host_data_t*)chip->host->driver_data)->spi != 0) {
// Encrypted operations have to use SPI0
return ESP_ERR_FLASH_UNSUPPORTED_HOST;
}
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->write_encrypted(chip, buffer, address, length);
return spiflash_end(chip, err);
}
inline static IRAM_ATTR bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len)
{
uint32_t a_end = a_start + a_len;
uint32_t b_end = b_start + b_len;
return (a_end > b_start && b_end > a_start);
}
/*------------------------------------------------------------------------------
Adapter layer to original api before IDF v4.0
------------------------------------------------------------------------------*/
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
static esp_err_t spi_flash_translate_rc(esp_err_t err)
{
switch (err) {
case ESP_OK:
return ESP_OK;
case ESP_ERR_INVALID_ARG:
return ESP_ERR_INVALID_ARG;
case ESP_ERR_FLASH_NOT_INITIALISED:
case ESP_ERR_FLASH_PROTECTED:
return ESP_ERR_INVALID_STATE;
case ESP_ERR_NOT_FOUND:
case ESP_ERR_FLASH_UNSUPPORTED_HOST:
case ESP_ERR_FLASH_UNSUPPORTED_CHIP:
return ESP_ERR_NOT_SUPPORTED;
case ESP_ERR_FLASH_NO_RESPONSE:
return ESP_ERR_INVALID_RESPONSE;
default:
ESP_EARLY_LOGE(TAG, "unexpected spi flash error code: %x", err);
abort();
}
return ESP_OK;
}
esp_err_t spi_flash_erase_range(uint32_t start_addr, uint32_t size)
{
esp_err_t err = esp_flash_erase_region(NULL, start_addr, size);
return spi_flash_translate_rc(err);
}
esp_err_t spi_flash_write(size_t dst, const void *srcv, size_t size)
{
esp_err_t err = esp_flash_write(NULL, srcv, dst, size);
return spi_flash_translate_rc(err);
}
esp_err_t spi_flash_read(size_t src, void *dstv, size_t size)
{
esp_err_t err = esp_flash_read(NULL, dstv, src, size);
return spi_flash_translate_rc(err);
}
esp_err_t spi_flash_unlock()
{
esp_err_t err = esp_flash_set_chip_write_protect(NULL, false);
return spi_flash_translate_rc(err);
}
#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL

View File

@@ -0,0 +1,206 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include "sdkconfig.h"
#include "esp_flash.h"
#include "memspi_host_driver.h"
#include "esp_flash_spi_init.h"
#include "driver/gpio.h"
#include "esp32/rom/spi_flash.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "hal/spi_types.h"
#include "driver/spi_common.h"
static const char TAG[] = "spi_flash";
#ifdef CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define DEFAULT_FLASH_SPEED ESP_FLASH_80MHZ
#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define DEFAULT_FLASH_SPEED ESP_FLASH_40MHZ
#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_26M
#define DEFAULT_FLASH_SPEED ESP_FLASH_26MHZ
#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define DEFAULT_FLASH_SPEED ESP_FLASH_20MHZ
#else
#error Flash frequency not defined! Check the ``CONFIG_ESPTOOLPY_FLASHFREQ_*`` options.
#endif
#if defined(CONFIG_ESPTOOLPY_FLASHMODE_QIO)
#define DEFAULT_FLASH_MODE SPI_FLASH_QIO
#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_QOUT)
#define DEFAULT_FLASH_MODE SPI_FLASH_QOUT
#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DIO)
#define DEFAULT_FLASH_MODE SPI_FLASH_DIO
#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DOUT)
#define DEFAULT_FLASH_MODE SPI_FLASH_DOUT
#else
#define DEFAULT_FLASH_MODE SPI_FLASH_FASTRD
#endif
static IRAM_ATTR void cs_initialize(esp_flash_t *chip, const esp_flash_spi_device_config_t *config, bool use_iomux)
{
//Not using spicommon_cs_initialize since we don't want to put the whole
//spi_periph_signal into the DRAM. Copy these data from flash before the
//cache disabling
int cs_io_num = config->cs_io_num;
int spics_in = spi_periph_signal[config->host_id].spics_in;
int spics_out = spi_periph_signal[config->host_id].spics_out[config->cs_id];
uint32_t iomux_reg = GPIO_PIN_MUX_REG[cs_io_num];
//To avoid the panic caused by flash data line conflicts during cs line
//initialization, disable the cache temporarily
chip->os_func->start(chip->os_func_data);
if (use_iomux) {
GPIO.func_in_sel_cfg[spics_in].sig_in_sel = 0;
PIN_INPUT_ENABLE(iomux_reg);
GPIO.func_out_sel_cfg[spics_out].oen_sel = 0;
GPIO.func_out_sel_cfg[spics_out].oen_inv_sel = false;
PIN_FUNC_SELECT(iomux_reg, 1);
} else {
PIN_INPUT_ENABLE(iomux_reg);
if (cs_io_num < 32) {
GPIO.enable_w1ts = (0x1 << cs_io_num);
} else {
GPIO.enable1_w1ts.data = (0x1 << (cs_io_num - 32));
}
GPIO.pin[cs_io_num].pad_driver = 0;
gpio_matrix_out(cs_io_num, spics_out, false, false);
if (config->cs_id == 0) {
gpio_matrix_in(cs_io_num, spics_in, false);
}
PIN_FUNC_SELECT(iomux_reg, PIN_FUNC_GPIO);
}
chip->os_func->end(chip->os_func_data);
}
esp_err_t spi_bus_add_flash_device(esp_flash_t **out_chip, const esp_flash_spi_device_config_t *config)
{
if (out_chip == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_flash_t *chip = NULL;
spi_flash_host_driver_t *host = NULL;
memspi_host_data_t *host_data = NULL;
esp_err_t ret = ESP_OK;
uint32_t caps = MALLOC_CAP_DEFAULT;
if (config->host_id == SPI_HOST) caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
chip = (esp_flash_t*)heap_caps_malloc(sizeof(esp_flash_t), caps);
host = (spi_flash_host_driver_t*)heap_caps_malloc(sizeof(spi_flash_host_driver_t), caps);
host_data = (memspi_host_data_t*)heap_caps_malloc(sizeof(memspi_host_data_t), caps);
if (!chip || !host || !host_data) {
ret = ESP_ERR_NO_MEM;
goto fail;
}
*chip = (esp_flash_t) {
.read_mode = config->io_mode,
.host = host,
};
esp_err_t err = esp_flash_init_os_functions(chip, config->host_id);
if (err != ESP_OK) {
ret = err;
goto fail;
}
bool use_iomux = spicommon_bus_using_iomux(config->host_id);
memspi_host_config_t host_cfg = {
.host_id = config->host_id,
.cs_num = config->cs_id,
.iomux = use_iomux,
.input_delay_ns = config->input_delay_ns,
.speed = config->speed,
};
err = memspi_host_init_pointers(host, host_data, &host_cfg);
if (err != ESP_OK) {
ret = err;
goto fail;
}
cs_initialize(chip, config, use_iomux);
*out_chip = chip;
return ret;
fail:
spi_bus_remove_flash_device(chip);
return ret;
}
esp_err_t spi_bus_remove_flash_device(esp_flash_t *chip)
{
if (chip==NULL) {
return ESP_ERR_INVALID_ARG;
}
if (chip->host) {
free(chip->host->driver_data);
free(chip->host);
}
free(chip);
return ESP_OK;
}
#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \
.host_id = SPI_HOST,\
.speed = DEFAULT_FLASH_SPEED, \
.cs_num = 0, \
.iomux = false, \
.input_delay_ns = 0,\
}
static DRAM_ATTR spi_flash_host_driver_t esp_flash_default_host_drv = ESP_FLASH_DEFAULT_HOST_DRIVER();
static DRAM_ATTR memspi_host_data_t default_driver_data;
/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */
extern const esp_flash_os_functions_t esp_flash_noos_functions;
static DRAM_ATTR esp_flash_t default_chip = {
.read_mode = DEFAULT_FLASH_MODE,
.host = &esp_flash_default_host_drv,
.os_func = &esp_flash_noos_functions,
};
esp_flash_t *esp_flash_default_chip = NULL;
esp_err_t esp_flash_init_default_chip()
{
memspi_host_config_t cfg = ESP_FLASH_HOST_CONFIG_DEFAULT();
//the host is already initialized, only do init for the data and load it to the host
spi_flash_hal_init(&default_driver_data, &cfg);
default_chip.host->driver_data = &default_driver_data;
// ROM TODO: account for non-standard default pins in efuse
// ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here
esp_err_t err = esp_flash_init(&default_chip);
if (err != ESP_OK) {
return err;
}
if (default_chip.size < g_rom_flashchip.chip_size) {
ESP_EARLY_LOGE(TAG, "Detected size(%dk) smaller than the size in the binary image header(%dk). Probe failed.", default_chip.size/1024, g_rom_flashchip.chip_size/1024);
return ESP_ERR_FLASH_SIZE_NOT_MATCH;
} else if (default_chip.size > g_rom_flashchip.chip_size) {
ESP_EARLY_LOGW(TAG, "Detected size(%dk) larger than the size in the binary image header(%dk). Using the size in the binary image header.", default_chip.size/1024, g_rom_flashchip.chip_size/1024);
default_chip.size = g_rom_flashchip.chip_size;
}
default_chip.size = g_rom_flashchip.chip_size;
esp_flash_default_chip = &default_chip;
return ESP_OK;
}
esp_err_t esp_flash_app_init()
{
return esp_flash_init_os_functions(&default_chip, 0);
}

View File

@@ -30,7 +30,6 @@
#include "esp_ipc.h"
#include "esp_attr.h"
#include "esp_spi_flash.h"
#include "esp_spi_flash_chip.h"
#include "esp_log.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/clk.h"
@@ -39,9 +38,8 @@
#include "soc/spi_mem_reg.h"
#endif
#include "esp_flash_partitions.h"
#include "esp_ota_ops.h"
#include "cache_utils.h"
#include "soc/spi_periph.h"
#include "esp_flash.h"
/* bytes erased by SPIEraseBlock() ROM function */
#define BLOCK_ERASE_SIZE 65536
@@ -84,7 +82,7 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = {
.end = spi_flash_enable_interrupts_caches_and_other_cpu,
.op_lock = spi_flash_op_lock,
.op_unlock = spi_flash_op_unlock,
#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
#if !CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
.is_safe_write_address = is_safe_write_address
#endif
};
@@ -94,14 +92,14 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = {
.end = spi_flash_enable_interrupts_caches_no_os,
.op_lock = 0,
.op_unlock = 0,
#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
#if !CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
.is_safe_write_address = 0
#endif
};
static const spi_flash_guard_funcs_t *s_flash_guard_ops;
#ifdef CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
#ifdef CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS
#define UNSAFE_WRITE_ADDRESS abort()
#else
#define UNSAFE_WRITE_ADDRESS return false
@@ -111,7 +109,7 @@ static const spi_flash_guard_funcs_t *s_flash_guard_ops;
/* CHECK_WRITE_ADDRESS macro to fail writes which land in the
bootloader, partition table, or running application region.
*/
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
#define CHECK_WRITE_ADDRESS(ADDR, SIZE)
#else /* FAILS or ABORTS */
#define CHECK_WRITE_ADDRESS(ADDR, SIZE) do { \
@@ -119,24 +117,14 @@ static const spi_flash_guard_funcs_t *s_flash_guard_ops;
return ESP_ERR_INVALID_ARG; \
} \
} while(0)
#endif // CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
#endif // CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size)
{
bool result = true;
if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) {
if (!esp_partition_main_flash_region_safe(addr, size)) {
UNSAFE_WRITE_ADDRESS;
}
const esp_partition_t *p = esp_ota_get_running_partition();
if (addr >= p->address && addr < p->address + p->size) {
UNSAFE_WRITE_ADDRESS;
}
if (addr < p->address && addr + size > p->address) {
UNSAFE_WRITE_ADDRESS;
}
return result;
return true;
}
@@ -191,7 +179,8 @@ static inline void IRAM_ATTR spi_flash_guard_op_unlock()
}
}
esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
{
static bool unlocked = false;
if (!unlocked) {
@@ -205,6 +194,16 @@ esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
#else
static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
{
esp_err_t err = esp_flash_set_chip_write_protect(NULL, false);
if (err != ESP_OK) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
#endif
esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
{
@@ -212,7 +211,9 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
}
esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
//deprecated, only used in compatible mode
esp_err_t IRAM_ATTR spi_flash_erase_range(size_t start_addr, size_t size)
{
CHECK_WRITE_ADDRESS(start_addr, size);
if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
@@ -423,10 +424,12 @@ out:
return spi_flash_translate_rc(rc);
}
#endif
esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size)
{
CHECK_WRITE_ADDRESS(dest_addr, size);
const uint8_t *ssrc = (const uint8_t *)src;
if ((dest_addr % 16) != 0) {
return ESP_ERR_INVALID_ARG;
}
@@ -436,7 +439,49 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src,
COUNTER_START();
esp_rom_spiflash_result_t rc;
rc = spi_flash_write_encrypted_chip(dest_addr, src, size);
rc = spi_flash_unlock();
if (rc == ESP_ROM_SPIFLASH_RESULT_OK) {
/* esp_rom_spiflash_write_encrypted encrypts data in RAM as it writes,
so copy to a temporary buffer - 32 bytes at a time.
Each call to esp_rom_spiflash_write_encrypted takes a 32 byte "row" of
data to encrypt, and each row is two 16 byte AES blocks
that share a key (as derived from flash address).
*/
uint8_t encrypt_buf[32] __attribute__((aligned(4)));
uint32_t row_size;
for (size_t i = 0; i < size; i += row_size) {
uint32_t row_addr = dest_addr + i;
if (i == 0 && (row_addr % 32) != 0) {
/* writing to second block of a 32 byte row */
row_size = 16;
row_addr -= 16;
/* copy to second block in buffer */
memcpy(encrypt_buf + 16, ssrc + i, 16);
/* decrypt the first block from flash, will reencrypt to same bytes */
spi_flash_read_encrypted(row_addr, encrypt_buf, 16);
} else if (size - i == 16) {
/* 16 bytes left, is first block of a 32 byte row */
row_size = 16;
/* copy to first block in buffer */
memcpy(encrypt_buf, ssrc + i, 16);
/* decrypt the second block from flash, will reencrypt to same bytes */
spi_flash_read_encrypted(row_addr + 16, encrypt_buf + 16, 16);
} else {
/* Writing a full 32 byte row (2 blocks) */
row_size = 32;
memcpy(encrypt_buf, ssrc + i, 32);
}
spi_flash_guard_start();
rc = esp_rom_spiflash_write_encrypted(row_addr, (uint32_t *)encrypt_buf, 32);
spi_flash_guard_end();
if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
break;
}
}
bzero(encrypt_buf, sizeof(encrypt_buf));
}
COUNTER_ADD_BYTES(write, size);
COUNTER_STOP(write);
@@ -447,6 +492,7 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src,
return spi_flash_translate_rc(rc);
}
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
{
// Out of bound reads are checked in ROM code, but we can give better
@@ -588,6 +634,7 @@ out:
COUNTER_STOP(read);
return spi_flash_translate_rc(rc);
}
#endif
esp_err_t IRAM_ATTR spi_flash_read_encrypted(size_t src, void *dstv, size_t size)
{
@@ -653,76 +700,3 @@ void spi_flash_dump_counters()
}
#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
#if CONFIG_IDF_TARGET_ESP32S2BETA
#define SPICACHE SPIMEM0
#define SPIFLASH SPIMEM1
#define FLASH_WRAP_CMD 0x77
esp_err_t spi_flash_wrap_set(spi_flash_wrap_mode_t mode)
{
uint32_t reg_bkp_ctrl = SPIFLASH.ctrl.val;
uint32_t reg_bkp_usr = SPIFLASH.user.val;
SPIFLASH.user.fwrite_dio = 0;
SPIFLASH.user.fwrite_dual = 0;
SPIFLASH.user.fwrite_qio = 1;
SPIFLASH.user.fwrite_quad = 0;
SPIFLASH.ctrl.fcmd_dual = 0;
SPIFLASH.ctrl.fcmd_quad = 0;
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 1;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = FLASH_WRAP_CMD;
SPIFLASH.user1.usr_addr_bitlen = 23;
SPIFLASH.addr = 0;
SPIFLASH.user.usr_miso = 0;
SPIFLASH.user.usr_mosi = 1;
SPIFLASH.mosi_dlen.usr_mosi_bit_len = 7;
SPIFLASH.data_buf[0] = (uint32_t) mode << 4;;
SPIFLASH.cmd.usr = 1;
while(SPIFLASH.cmd.usr != 0)
{ }
SPIFLASH.ctrl.val = reg_bkp_ctrl;
SPIFLASH.user.val = reg_bkp_usr;
return ESP_OK;
}
esp_err_t spi_flash_enable_wrap(uint32_t wrap_size)
{
switch(wrap_size) {
case 8:
return spi_flash_wrap_set(FLASH_WRAP_MODE_8B);
case 16:
return spi_flash_wrap_set(FLASH_WRAP_MODE_16B);
case 32:
return spi_flash_wrap_set(FLASH_WRAP_MODE_32B);
case 64:
return spi_flash_wrap_set(FLASH_WRAP_MODE_64B);
default:
return ESP_FAIL;
}
}
void spi_flash_disable_wrap()
{
spi_flash_wrap_set(FLASH_WRAP_MODE_DISABLE);
}
bool spi_flash_support_wrap_size(uint32_t wrap_size)
{
if (!REG_GET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FREAD_QIO) || !REG_GET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FASTRD_MODE)){
return ESP_FAIL;
}
switch(wrap_size) {
case 0:
case 8:
case 16:
case 32:
case 64:
return true;
default:
return false;
}
}
#endif

View File

@@ -0,0 +1,300 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 "esp_err.h"
#include <stdint.h>
#include <stdbool.h>
#include "hal/spi_flash_types.h"
#ifdef __cplusplus
extern "C" {
#endif
struct spi_flash_chip_t;
typedef struct spi_flash_chip_t spi_flash_chip_t;
typedef struct esp_flash_t esp_flash_t;
/** @brief Structure for describing a region of flash */
typedef struct {
uint32_t offset; ///< Start address of this region
uint32_t size; ///< Size of the region
} esp_flash_region_t;
/** OS-level integration hooks for accessing flash chips inside a running OS */
typedef struct {
/**
* Called before commencing any flash operation. Does not need to be
* recursive (ie is called at most once for each call to 'end').
*/
esp_err_t (*start)(void *arg);
/** Called after completing any flash operation. */
esp_err_t (*end)(void *arg);
/** Delay for at least 'ms' milliseconds. Called in between 'start' and 'end'. */
esp_err_t (*delay_ms)(void *arg, unsigned ms);
} esp_flash_os_functions_t;
/** @brief Structure to describe a SPI flash chip connected to the system.
Structure must be initialized before use (passed to esp_flash_init()).
*/
struct esp_flash_t {
spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. Must be initialized before used.
const spi_flash_chip_t *chip_drv; ///< Pointer to chip-model-specific "adapter" structure. If NULL, will be detected during initialisation.
const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hook structure. Call ``esp_flash_init_os_functions()`` to setup this field, after the host is properly initialized.
void *os_func_data; ///< Pointer to argument for os-specific hooks. Left NULL and will be initialized with ``os_func``.
esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before ``esp_flash_init`` is called.
uint32_t size; ///< Size of SPI flash in bytes. If 0, size will be detected during initialisation.
};
/** @brief Initialise SPI flash chip interface.
*
* This function must be called before any other API functions are called for this chip.
*
* @note Only the ``host`` and ``read_mode`` fields of the chip structure must
* be initialised before this function is called. Other fields may be
* auto-detected if left set to zero or NULL.
*
* @note If the chip->drv pointer is NULL, chip chip_drv will be auto-detected
* based on its manufacturer & product IDs. See
* ``esp_flash_registered_flash_drivers`` pointer for details of this process.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @return ESP_OK on success, or a flash error code if initialisation fails.
*/
esp_err_t esp_flash_init(esp_flash_t *chip);
/**
* Check if appropriate chip driver is set.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return true if set, otherwise false.
*/
bool esp_flash_chip_driver_initialized(const esp_flash_t *chip);
/** @brief Read flash ID via the common "RDID" SPI flash command.
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param[out] out_id Pointer to receive ID value.
*
* ID is a 24-bit value. Lower 16 bits of 'id' are the chip ID, upper 8 bits are the manufacturer ID.
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_read_id(esp_flash_t *chip, uint32_t *out_id);
/** @brief Detect flash size based on flash ID.
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param[out] out_size Detected size in bytes.
*
* @note Most flash chips use a common format for flash ID, where the lower 4 bits specify the size as a power of 2. If
* the manufacturer doesn't follow this convention, the size may be incorrectly detected.
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size);
/** @brief Erase flash chip contents
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
*
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_erase_chip(esp_flash_t *chip);
/** @brief Erase a region of the flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param start Address to start erasing flash. Must be sector aligned.
* @param len Length of region to erase. Must also be sector aligned.
*
* Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be
* returned if the start & length are not a multiple of this size.
*
* Erase is performed using block (multi-sector) erases where possible (block size is specified in
* chip->drv->block_erase_size field, typically 65536 bytes). Remaining sectors are erased using individual sector erase
* commands.
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len);
/** @brief Read if the entire chip is write protected
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param[out] write_protected Pointer to boolean, set to the value of the write protect flag.
*
* @note A correct result for this flag depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protected);
/** @brief Set write protection for the SPI flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param write_protect Boolean value for the write protect flag
*
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* If write protection is enabled, destructive operations will fail with ESP_ERR_FLASH_PROTECTED.
*
* Some SPI flash chips may require a power cycle before write protect status can be cleared. Otherwise,
* write protection can be removed via a follow-up call to this function.
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect);
/** @brief Read the list of individually protectable regions of this SPI flash chip.
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param[out] out_regions Pointer to receive a pointer to the array of protectable regions of the chip.
* @param[out] out_num_regions Pointer to an integer receiving the count of protectable regions in the array returned in 'regions'.
*
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **out_regions, uint32_t *out_num_regions);
/** @brief Detect if a region of the SPI flash chip is protected
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param region Pointer to a struct describing a protected region. This must match one of the regions returned from esp_flash_get_protectable_regions(...).
* @param[out] out_protected Pointer to a flag which is set based on the protected status for this region.
*
* @note It is possible for this result to be false and write operations to still fail, if protection is enabled for the entire chip.
*
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *out_protected);
/** @brief Update the protected status for a region of the SPI flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param region Pointer to a struct describing a protected region. This must match one of the regions returned from esp_flash_get_protectable_regions(...).
* @param protect Write protection flag to set.
*
* @note It is possible for the region protection flag to be cleared and write operations to still fail, if protection is enabled for the entire chip.
*
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protect);
/** @brief Read data from the SPI flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param buffer Pointer to a buffer where the data will be read. To get better performance, this should be in the DRAM and word aligned.
* @param address Address on flash to read from. Must be less than chip->size field.
* @param length Length (in bytes) of data to read.
*
* There are no alignment constraints on buffer, address or length.
*
* @note If on-chip flash encryption is used, this function returns raw (ie encrypted) data. Use the flash cache
* to transparently decrypt data.
*
* @return
* - ESP_OK: success
* - ESP_ERR_NO_MEM: the buffer is not valid, however failed to malloc on
* the heap.
* - or a flash error code if operation failed.
*/
esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);
/** @brief Write data to the SPI flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param address Address on flash to write to. Must be previously erased (SPI NOR flash can only write bits 1->0).
* @param buffer Pointer to a buffer with the data to write. To get better performance, this should be in the DRAM and word aligned.
* @param length Length (in bytes) of data to write.
*
* There are no alignment constraints on buffer, address or length.
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/** @brief Encrypted and write data to the SPI flash chip using on-chip hardware flash encryption
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param address Address on flash to write to. 16 byte aligned. Must be previously erased (SPI NOR flash can only write bits 1->0).
* @param buffer Pointer to a buffer with the data to write.
* @param length Length (in bytes) of data to write. 16 byte aligned.
*
* @note Both address & length must be 16 byte aligned, as this is the encryption block size
*
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_err_t esp_flash_write_encrypted(esp_flash_t *chip, uint32_t address, const void *buffer, uint32_t length);
/** @brief Pointer to the "default" SPI flash chip, ie the main chip attached to the MCU.
This chip is used if the 'chip' argument pass to esp_flash_xxx API functions is ever NULL.
*/
extern esp_flash_t *esp_flash_default_chip;
/** @brief Initialise the default SPI flash chip
*
* Called by OS startup code. You do not need to call this in your own applications.
*/
esp_err_t esp_flash_init_default_chip();
/**
* Enable OS-level SPI flash protections in IDF
*
* Called by OS startup code. You do not need to call this in your own applications.
*
* @return ESP_OK if success, otherwise failed. See return value of ``esp_flash_init_os_functions``.
*/
esp_err_t esp_flash_app_init();
/**
* Enable OS-level SPI flash for a specific chip.
*
* @param chip The chip to init os functions.
* @param host_id Which SPI host to use, 1 for SPI1, 2 for SPI2 (HSPI), 3 for SPI3 (VSPI)
*
* @return
* - ESP_OK if success
* - ESP_ERR_INVALID_ARG if host_id is invalid
*/
esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,55 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 "hal/spi_types.h"
#include "esp_flash.h"
/// Configurations for the SPI Flash to init
typedef struct {
spi_host_device_t host_id; ///< Bus to use
int cs_id; ///< CS pin (signal) to use
int cs_io_num; ///< GPIO pin to output the CS signal
esp_flash_read_mode_t io_mode; ///< IO mode to read from the Flash
esp_flash_speed_t speed; ///< Speed of the Flash clock
int input_delay_ns; ///< Input delay of the data pins, in ns. Set to 0 if unknown.
} esp_flash_spi_device_config_t;
/**
* Add a SPI Flash device onto the SPI bus.
*
* The bus should be already initialized by ``spi_bus_initialization``.
*
* @param out_chip Pointer to hold the initialized chip.
* @param config Configuration of the chips to initialize.
*
* @return
* - ESP_ERR_INVALID_ARG: out_chip is NULL, or some field in the config is invalid.
* - ESP_ERR_NO_MEM: failed to allocate memory for the chip structures.
* - ESP_OK: success.
*/
esp_err_t spi_bus_add_flash_device(esp_flash_t **out_chip, const esp_flash_spi_device_config_t *config);
/**
* Remove a SPI Flash device from the SPI bus.
*
* @param chip The flash device to remove.
*
* @return
* - ESP_ERR_INVALID_ARG: The chip is invalid.
* - ESP_OK: success.
*/
esp_err_t spi_bus_remove_flash_device(esp_flash_t *chip);

View File

@@ -19,8 +19,10 @@
#include <stdbool.h>
#include <stddef.h>
#include "esp_err.h"
#include "esp_flash.h"
#include "esp_spi_flash.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -98,6 +100,7 @@ typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t;
* However, this is the format used by this API.
*/
typedef struct {
esp_flash_t* flash_chip; /*!< SPI flash chip on which the partition resides */
esp_partition_type_t type; /*!< partition type (app/data) */
esp_partition_subtype_t subtype; /*!< partition subtype */
uint32_t address; /*!< starting address of the partition in flash */
@@ -246,8 +249,8 @@ esp_err_t esp_partition_write(const esp_partition_t* partition,
* @param partition Pointer to partition structure obtained using
* esp_partition_find_first or esp_partition_get.
* Must be non-NULL.
* @param start_addr Address where erase operation should start. Must be aligned
* to 4 kilobytes.
* @param offset Offset from the beginning of partition where erase operation
* should start. Must be aligned to 4 kilobytes.
* @param size Size of the range which should be erased, in bytes.
* Must be divisible by 4 kilobytes.
*
@@ -257,7 +260,7 @@ esp_err_t esp_partition_write(const esp_partition_t* partition,
* or one of error codes from lower-level flash driver.
*/
esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
uint32_t start_addr, uint32_t size);
size_t offset, size_t size);
/**
* @brief Configure MMU to map partition into data memory
@@ -284,7 +287,7 @@ esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
*
* @return ESP_OK, if successful
*/
esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size,
esp_err_t esp_partition_mmap(const esp_partition_t* partition, size_t offset, size_t size,
spi_flash_mmap_memory_t memory,
const void** out_ptr, spi_flash_mmap_handle_t* out_handle);
@@ -320,6 +323,43 @@ esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sh
*/
bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2);
/**
* @brief Register a partition on an external flash chip
*
* This API allows designating certain areas of external flash chips (identified by the esp_flash_t structure)
* as partitions. This allows using them with components which access SPI flash through the esp_partition API.
*
* @param flash_chip Pointer to the structure identifying the flash chip
* @param offset Address in bytes, where the partition starts
* @param size Size of the partition in bytes
* @param label Partition name
* @param type One of the partition types (ESP_PARTITION_TYPE_*). Note that applications can not be booted from external flash
* chips, so using ESP_PARTITION_TYPE_APP is not supported.
* @param subtype One of the partition subtypes (ESP_PARTITION_SUBTYPE_*)
* @param[out] out_partition Output, if non-NULL, receives the pointer to the resulting esp_partition_t structure
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_SUPPORTED if CONFIG_CONFIG_SPI_FLASH_USE_LEGACY_IMPL is enabled
* - ESP_ERR_NO_MEM if memory allocation has failed
* - ESP_ERR_INVALID_ARG if the new partition overlaps another partition on the same flash chip
* - ESP_ERR_INVALID_SIZE if the partition doesn't fit into the flash chip size
*/
esp_err_t esp_partition_register_external(esp_flash_t* flash_chip, size_t offset, size_t size,
const char* label, esp_partition_type_t type, esp_partition_subtype_t subtype,
const esp_partition_t** out_partition);
/**
* @brief Deregister the partition previously registered using esp_partition_register_external
* @param partition pointer to the partition structure obtained from esp_partition_register_external,
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_FOUND if the partition pointer is not found
* - ESP_ERR_INVALID_ARG if the partition comes from the partition table
* - ESP_ERR_INVALID_ARG if the partition was not registered using
* esp_partition_register_external function.
*/
esp_err_t esp_partition_deregister_external(const esp_partition_t* partition);
#ifdef __cplusplus
}
#endif

View File

@@ -25,7 +25,6 @@
extern "C" {
#endif
#define ESP_ERR_FLASH_BASE 0x10010
#define ESP_ERR_FLASH_OP_FAIL (ESP_ERR_FLASH_BASE + 1)
#define ESP_ERR_FLASH_OP_TIMEOUT (ESP_ERR_FLASH_BASE + 2)
@@ -354,7 +353,7 @@ typedef bool (*spi_flash_is_safe_write_address_t)(size_t addr, size_t size);
* - 'op_unlock' unlocks access to flash API internal data.
* These two functions are recursive and can be used around the outside of multiple calls to
* 'start' & 'end', in order to create atomic multi-part flash operations.
* 3) When CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is disabled, flash writing/erasing
* 3) When CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is disabled, flash writing/erasing
* API checks for addresses provided by user to avoid corruption of critical flash regions
* (bootloader, partition table, running application etc.).
*
@@ -372,7 +371,7 @@ typedef struct {
spi_flash_guard_end_func_t end; /**< critical section end function. */
spi_flash_op_lock_func_t op_lock; /**< flash access API lock function.*/
spi_flash_op_unlock_func_t op_unlock; /**< flash access API unlock function.*/
#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
#if !CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
spi_flash_is_safe_write_address_t is_safe_write_address; /**< checks flash write addresses.*/
#endif
} spi_flash_guard_funcs_t;

View File

@@ -0,0 +1,111 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 "hal/spi_flash_hal.h"
/** Default configuration for the memspi (high speed version) */
#define ESP_FLASH_DEFAULT_HOST_DRIVER() (spi_flash_host_driver_t) { \
.dev_config = spi_flash_hal_device_config, \
.common_command = spi_flash_hal_common_command, \
.read_id = memspi_host_read_id_hs, \
.erase_chip = spi_flash_hal_erase_chip, \
.erase_sector = spi_flash_hal_erase_sector, \
.erase_block = spi_flash_hal_erase_block, \
.read_status = memspi_host_read_status_hs, \
.set_write_protect = spi_flash_hal_set_write_protect, \
.supports_direct_write = spi_flash_hal_supports_direct_write, \
.supports_direct_read = spi_flash_hal_supports_direct_read, \
.program_page = spi_flash_hal_program_page, \
.max_write_bytes = SPI_FLASH_HAL_MAX_WRITE_BYTES, \
.read = spi_flash_hal_read, \
.max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES, \
.host_idle = spi_flash_hal_host_idle, \
.configure_host_read_mode = spi_flash_hal_configure_host_read_mode, \
.poll_cmd_done = spi_flash_hal_poll_cmd_done, \
.flush_cache = memspi_host_flush_cache, \
.region_protected = memspi_region_protected, \
}
/// configuration for the memspi host
typedef spi_flash_memspi_config_t memspi_host_config_t;
/// context for the memspi host
typedef spi_flash_memspi_data_t memspi_host_data_t;
/**
* Initialize the memory SPI host.
*
* @param host Pointer to the host structure.
* @param data Pointer to allocated space to hold the context of host driver.
* @param cfg Pointer to configuration structure
*
* @return always return ESP_OK
*/
esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_data_t *data, const memspi_host_config_t *cfg);
/*******************************************************************************
* NOTICE
* Rest part of this file are part of the HAL layer
* The HAL is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
/**
* High speed implementation of RDID through memspi interface relying on the
* ``common_command``.
*
* @param driver The driver context.
* @param id Output of the read ID from the slave.
*
* @return
* - ESP_OK: if success
* - ESP_ERR_FLASH_NO_RESPONSE: if no response from chip
* - or other cases from ``spi_hal_common_command``
*/
esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *driver, uint32_t *id);
/**
* High speed implementation of RDSR through memspi interface relying on the
* ``common_command``.
*
* @param driver The driver context.
* @param id Output of the read ID from the slave.
*
* @return
* - ESP_OK: if success
* - or other cases from ``spi_hal_common_command``
*/
esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *out_sr);
/**
* Flush the cache (if needed) after the contents are modified.
*
* @param driver The driver context.
* @param addr Start address of the modified region
* @param size Size of the region modified.
*
* @return always ESP_OK.
*/
esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size);
/**
* Check if the given region is protected.
*
* @param driver The driver context.
* @param addr Start address of the region.
* @param size Size of the region to check.
*
* @return true if protected, otherwise false.
*/
bool memspi_region_protected(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size);

View File

@@ -0,0 +1,165 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 "esp_flash.h"
struct esp_flash_t;
typedef struct esp_flash_t esp_flash_t;
typedef struct spi_flash_chip_t spi_flash_chip_t;
/** @brief SPI flash chip driver definition structure.
*
* The chip driver structure contains chip-specific pointers to functions to perform SPI flash operations, and some
* chip-specific numeric values.
*
* @note This is not a public API. These functions are called from the public API (declared in
* esp_flash.h). They assume the caller has already validated arguments and enabled relevant protections
* (disabling flash cache, prevent concurrent SPI access, etc.)
*
* Do not call chip driver functions directly in other contexts.
*
* A generic driver for generic chips and its related operations are defined in
* spi_flash_chip_generic.h which can be used as building blocks for written
* new/specific SPI flash chip drivers.
*
* @note All of these functions may be called with SPI flash cache disabled, so must only ever access IRAM/DRAM/ROM.
*/
struct spi_flash_chip_t {
const char *name; ///< Name of the chip driver
/* Probe to detect if a supported SPI flash chip is found.
*
* Attempts to configure 'chip' with these operations and probes for a matching SPI flash chip.
*
* Auto-detection of a SPI flash chip calls this function in turn on each registered driver (see esp_flash_registered_flash_drivers).
*
* ID - as read by spi_flash_generic_read_id() - is supplied so each probe
* function doesn't need to unnecessarily read ID, but probe is permitted
* to interrogate flash in any non-destructive way.
*
* It is permissible for the driver to modify the 'chip' structure if probing succeeds (specifically, to assign something to the
* driver_data pointer if that is useful for the driver.)
*
* @return ESP_OK if probing was successful, an error otherwise. Driver may
* assume that returning ESP_OK means it has claimed this chip.
*/
esp_err_t (*probe)(esp_flash_t *chip, uint32_t flash_id);
esp_err_t (*reset)(esp_flash_t *chip);
/* Detect SPI flash size
*
* Interrogate the chip to detect its size.
*/
esp_err_t (*detect_size)(esp_flash_t *chip, uint32_t *size);
/* Erase the entire chip
Caller has verified the chip is not write protected.
*/
esp_err_t (*erase_chip)(esp_flash_t *chip);
/* Erase a sector of the chip. Sector size is specified in the 'sector_size' field.
sector_address is an offset in bytes.
Caller has verified that this sector should be non-write-protected.
*/
esp_err_t (*erase_sector)(esp_flash_t *chip, uint32_t sector_address);
/* Erase a multi-sector block of the chip. Block size is specified in the 'block_erase_size' field.
sector_address is an offset in bytes.
Caller has verified that this block should be non-write-protected.
*/
esp_err_t (*erase_block)(esp_flash_t *chip, uint32_t block_address);
uint32_t sector_size; /* Sector is minimum erase size */
uint32_t block_erase_size; /* Optimal (fastest) block size for multi-sector erases on this chip */
/* Read the write protect status of the entire chip. */
esp_err_t (*get_chip_write_protect)(esp_flash_t *chip, bool *write_protected);
/* Set the write protect status of the entire chip. */
esp_err_t (*set_chip_write_protect)(esp_flash_t *chip, bool write_protect_chip);
/* Number of individually write protectable regions on this chip. Range 0-63. */
uint8_t num_protectable_regions;
/* Pointer to an array describing each protectable region. Should have num_protectable_regions elements. */
const esp_flash_region_t *protectable_regions;
/* Get a bitmask describing all protectable regions on the chip. Each bit represents one entry in the
protectable_regions array, ie bit (1<<N) is set then the region at array entry N is write protected. */
esp_err_t (*get_protected_regions)(esp_flash_t *chip, uint64_t *regions);
/* Set protectable regions on the chip. Each bit represents on entry in the protectable regions array. */
esp_err_t (*set_protected_regions)(esp_flash_t *chip, uint64_t regions);
/* Read data from the chip.
*
* Before calling this function, the caller will have called chip->drv->set_read_mode(chip) in order to configure the chip's read mode correctly.
*/
esp_err_t (*read)(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);
/* Write any amount of data to the chip.
*/
esp_err_t (*write)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/* Use the page program command to write data to the chip.
*
* This function is expected to be called by chip->drv->write (if the
* chip->drv->write implementation doesn't call it then it can be left as NULL.)
*
* - The length argument supplied to this function is at most 'page_size' bytes.
*
* - The region between 'address' and 'address + length' will not cross a page_size aligned boundary (the write
* implementation is expected to split such a write into two before calling page_program.)
*/
esp_err_t (*program_page)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/* Page size as written by the page_program function. Usually 256 bytes. */
uint32_t page_size;
/* Perform an encrypted write to the chip, using internal flash encryption hardware. */
esp_err_t (*write_encrypted)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/* Set the write enable flag. This function is called internally by other functions in this structure, before a destructive
operation takes place. */
esp_err_t (*set_write_protect)(esp_flash_t *chip, bool write_protect);
/* Wait for the SPI flash chip to be idle (any write operation to be complete.) This function is both called from the higher-level API functions, and from other functions in this structure.
timeout_ms should be a timeout (in milliseconds) before the function returns ESP_ERR_TIMEOUT. This is useful to avoid hanging
if the chip is otherwise unresponsive (ie returns all 0xFF or similar.)
*/
esp_err_t (*wait_idle)(esp_flash_t *chip, unsigned timeout_ms);
/* Configure both the SPI host and the chip for the read mode specified in chip->read_mode.
*
* This function is called by the higher-level API before the 'read' function is called.
*
* Can return ESP_ERR_FLASH_UNSUPPORTED_HOST or ESP_ERR_FLASH_UNSUPPORTED_CHIP if the specified mode is unsupported.
*/
esp_err_t (*set_read_mode)(esp_flash_t *chip);
};
/* Pointer to an array of pointers to all known drivers for flash chips. This array is used
by esp_flash_init() to detect the flash chip driver, if none is supplied by the caller.
Array is terminated with a NULL pointer.
This pointer can be overwritten with a pointer to a new array, to update the list of known flash chips.
*/
extern const spi_flash_chip_t **esp_flash_registered_chips;

View File

@@ -0,0 +1,275 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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 <stdint.h>
#include "esp_flash.h"
#include "spi_flash_chip_driver.h"
/*
* The 'chip_generic' SPI flash operations are a lowest common subset of SPI
* flash commands, that work across most chips.
*
* These can be used as-is via the esp_flash_common_chip_driver chip_drv, or
* they can be used as "base chip_drv" functions when creating a new
* spi_flash_host_driver_t chip_drv structure.
*
* All of the functions in this header are internal functions, not part of a
* public API. See esp_flash.h for the public API.
*/
/**
* @brief Generic probe function
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param flash_id expected manufacture id.
*
* @return ESP_OK if the id read from chip->drv_read_id matches (always).
*/
esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id);
/**
* @brief Generic reset function
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return ESP_OK if sending success, or error code passed from ``common_command`` or ``wait_idle`` functions of host driver.
*/
esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip);
/**
* @brief Generic size detection function
*
* Tries to detect the size of chip by using the lower 4 bits of the chip->drv->read_id result = N, and assuming size is 2 ^ N.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param size Output of the detected size
*
* @return
* - ESP_OK if success
* - ESP_ERR_FLASH_UNSUPPORTED_CHIP if the manufacturer id is not correct, which may means an error in the reading
* - or other error passed from the ``read_id`` function of host driver
*/
esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size);
/**
* @brief Erase chip by using the generic erase chip (C7h) command.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_chip`` function of host driver
*/
esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip);
/**
* @brief Erase sector by using the generic sector erase (20h) command.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param start_address Start address of the sector to erase
*
* @return
* - ESP_OK if success
* - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_sector`` function of host driver
*/
esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_address);
/**
* @brief Erase block by using the generic 64KB block erase (D8h) command
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param start_address Start address of the block to erase
*
* @return
* - ESP_OK if success
* - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_block`` function of host driver
*/
esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_address);
/**
* @brief Read from flash by using a read command that matches the programmed
* read mode.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer to hold the data read from flash
* @param address Start address of the data on the flash
* @param length Length to read
*
* @return always ESP_OK currently
*/
esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);
/**
* @brief Perform a page program using the page program (02h) command.
*
* @note Length of each call should not excced the limitation in
* ``chip->host->max_write_bytes``. This function is called in
* ``spi_flash_chip_generic_write`` recursively until the whole page is
* programmed. Strongly suggest to call ``spi_flash_chip_generic_write``
* instead.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer holding the data to program
* @param address Start address to write to flash
* @param length Length to write, no longer than ``chip->host->max_write_bytes``.
*
* @return
* - ESP_OK if success
* - or other error passed from the ``wait_idle`` or ``program_page`` function of host driver
*/
esp_err_t
spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/**
* @brief Perform a generic write. Split the write buffer into page program
* operations, and call chip->chip_drv->page-program() for each.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer holding the data to program
* @param address Start address to write to flash
* @param length Length to write
*
* @return
* - ESP_OK if success
* - or other error passed from the ``wait_idle`` or ``program_page`` function of host driver
*/
esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/**
* @brief Perform a write using on-chip flash encryption. Not implemented yet.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer holding the data to program
* @param address Start address to write to flash
* @param length Length to write
*
* @return always ESP_ERR_FLASH_UNSUPPORTED_HOST.
*/
esp_err_t
spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/**
* @brief Send the write enable (06h) command and verify the expected bit (1) in
* the status register is set.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param write_protect true to enable write protection, false to send write enable.
*
* @return
* - ESP_OK if success
* - or other error passed from the ``wait_idle``, ``read_status`` or ``set_write_protect`` function of host driver
*/
esp_err_t spi_flash_chip_generic_write_enable(esp_flash_t *chip, bool write_protect);
/**
* @brief Read flash status via the RDSR command (05h) and wait for bit 0 (write
* in progress bit) to be cleared.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param timeout_ms Time to wait before timeout, in ms.
*
* @return
* - ESP_OK if success
* - ESP_ERR_TIMEOUT if not idle before timeout
* - or other error passed from the ``wait_idle`` or ``read_status`` function of host driver
*/
esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_ms);
/**
* @brief Set the specified SPI read mode according to the data in the chip
* context. Set quad enable status register bit if needed.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - ESP_ERR_TIMEOUT if not idle before timeout
* - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver
*/
esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip);
/**
* Generic SPI flash chip_drv, uses all the above functions for its operations.
* In default autodetection, this is used as a catchall if a more specific
* chip_drv is not found.
*/
extern const spi_flash_chip_t esp_flash_chip_generic;
/*******************************************************************************
* Utilities
*******************************************************************************/
/**
* @brief Wait for the SPI host hardware state machine to be idle.
*
* This isn't a flash chip_drv operation, but it's called by
* spi_flash_chip_generic_wait_idle() and may be useful when implementing
* alternative drivers.
*
* timeout_ms will be decremented if the function needs to wait until the host hardware is idle.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - ESP_ERR_TIMEOUT if not idle before timeout
* - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver
*/
esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ms);
/**
* @brief Utility function for set_read_mode chip_drv function
*
* Most setting of read mode follows a common pattern, except for how to enable Quad I/O modes (QIO/QOUT).
* These use different commands to read/write the status register, and a different bit is set/cleared.
*
* This is a generic utility function to implement set_read_mode() for this pattern. Also configures host
* registers via spi_flash_common_configure_host_read_mode().
*
* @param qe_rdsr_command SPI flash command to read status register
* @param qe_wrsr_command SPI flash command to write status register
* @param qe_sr_bitwidth Width of the status register these commands operate on, in bits.
* @param qe_sr_bit Bit mask for enabling Quad Enable functions on this chip.
*
* @return always ESP_OK (currently).
*/
esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit);
/**
* @brief Configure the host to use the specified read mode set in the ``chip->read_mode``.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - ESP_ERR_FLASH_NOT_INITIALISED if chip not initialized properly
* - or other error passed from the ``configure_host_mode`` function of host driver
*/
esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip);
/**
* @brief Returns true if chip is configured for Quad I/O or Quad Fast Read.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return true if flash works in quad mode, otherwise false
*/
static inline bool spi_flash_is_quad_mode(const esp_flash_t *chip)
{
return (chip->read_mode == SPI_FLASH_QIO) || (chip->read_mode == SPI_FLASH_QOUT);
}

View File

@@ -1,4 +1,4 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,20 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/* Private header with chip-specific routines for SPI flash interaction */
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/spi_flash.h"
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#include "esp32s2beta/rom/spi_flash.h"
#endif
#include "esp_flash.h"
#include "spi_flash_chip_driver.h"
esp_rom_spiflash_result_t spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size);
esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock();
/**
* ISSI SPI flash chip_drv, uses all the above functions for its operations. In
* default autodetection, this is used as a catchall if a more specific chip_drv
* is not found.
*/
extern const spi_flash_chip_t esp_flash_chip_issi;

View File

@@ -1,5 +1,8 @@
[mapping:spi_flash]
archive: libspi_flash.a
entries:
entries:
spi_flash_rom_patch (noflash_text)
spi_flash_chip_generic (noflash)
spi_flash_chip_issi (noflash)
memspi_host_driver (noflash)

View File

@@ -0,0 +1,101 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include "spi_flash_defs.h"
#include "memspi_host_driver.h"
#include "string.h"
#include "esp_log.h"
#include "cache_utils.h"
#include "esp_flash_partitions.h"
static const char TAG[] = "memspi";
static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER();
esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_data_t *data, const memspi_host_config_t *cfg)
{
memcpy(host, &esp_flash_default_host, sizeof(spi_flash_host_driver_t));
esp_err_t err = spi_flash_hal_init(data, cfg);
if (err != ESP_OK) {
return err;
}
host->driver_data = data;
//some functions are not required if not SPI1
if (data->spi != &SPI1) {
host->flush_cache = NULL;
host->region_protected = NULL;
}
return ESP_OK;
}
esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *chip_drv, uint32_t *id)
{
//NOTE: we do have a read id function, however it doesn't work in high freq
spi_flash_trans_t t = {
.command = CMD_RDID,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = 24
};
chip_drv->common_command(chip_drv, &t);
uint32_t raw_flash_id = t.miso_data[0];
ESP_EARLY_LOGV(TAG, "raw_chip_id: %X\n", raw_flash_id);
if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) {
ESP_EARLY_LOGE(TAG, "no response\n");
return ESP_ERR_FLASH_NO_RESPONSE;
}
// Byte swap the flash id as it's usually written the other way around
uint8_t mfg_id = raw_flash_id & 0xFF;
uint16_t flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00);
*id = ((uint32_t)mfg_id << 16) | flash_id;
ESP_EARLY_LOGV(TAG, "chip_id: %X\n", *id);
return ESP_OK;
}
esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *out_sr)
{
//NOTE: we do have a read id function, however it doesn't work in high freq
spi_flash_trans_t t = {
.command = CMD_RDSR,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = 8
};
esp_err_t err = driver->common_command(driver, &t);
if (err != ESP_OK) {
return err;
}
*out_sr = t.miso_data[0];
return ESP_OK;
}
esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size)
{
if (((memspi_host_data_t*)(driver->driver_data))->spi == &SPI1) {
spi_flash_check_and_flush_cache(addr, size);
}
return ESP_OK;
}
bool memspi_region_protected(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size)
{
if (((memspi_host_data_t*)(driver->driver_data))->spi != &SPI1) {
return false;
}
if (!esp_partition_main_flash_region_safe(addr, size)) {
return true;
}
return false;
}

View File

@@ -19,11 +19,14 @@
#include <sys/lock.h>
#include "esp_flash_partitions.h"
#include "esp_attr.h"
#include "esp_flash.h"
#include "esp_spi_flash.h"
#include "esp_partition.h"
#include "esp_flash_encrypt.h"
#include "esp_log.h"
#include "bootloader_common.h"
#include "bootloader_util.h"
#include "esp_ota_ops.h"
#define HASH_LEN 32 /* SHA-256 digest length */
@@ -34,8 +37,10 @@
#include "sys/queue.h"
typedef struct partition_list_item_ {
esp_partition_t info;
bool user_registered;
SLIST_ENTRY(partition_list_item_) next;
} partition_list_item_t;
@@ -162,12 +167,18 @@ static esp_err_t load_partitions()
break;
}
// allocate new linked list item and populate it with data from partition table
partition_list_item_t* item = (partition_list_item_t*) malloc(sizeof(partition_list_item_t));
partition_list_item_t* item = (partition_list_item_t*) calloc(sizeof(partition_list_item_t), 1);
if (item == NULL) {
err = ESP_ERR_NO_MEM;
break;
}
item->info.flash_chip = esp_flash_default_chip;
item->info.address = it->pos.offset;
item->info.size = it->pos.size;
item->info.type = it->type;
item->info.subtype = it->subtype;
item->info.encrypted = it->flags & PART_FLAG_ENCRYPTED;
item->user_registered = false;
if (!esp_flash_encryption_enabled()) {
/* If flash encryption is not turned on, no partitions should be treated as encrypted */
@@ -192,7 +203,7 @@ static esp_err_t load_partitions()
last = item;
}
spi_flash_munmap(handle);
return ESP_OK;
return err;
}
void esp_partition_iterator_release(esp_partition_iterator_t iterator)
@@ -207,6 +218,80 @@ const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator)
return iterator->info;
}
esp_err_t esp_partition_register_external(esp_flash_t* flash_chip, size_t offset, size_t size,
const char* label, esp_partition_type_t type, esp_partition_subtype_t subtype,
const esp_partition_t** out_partition)
{
if (out_partition != NULL) {
*out_partition = NULL;
}
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
return ESP_ERR_NOT_SUPPORTED;
#endif
if (offset + size > flash_chip->size) {
return ESP_ERR_INVALID_SIZE;
}
partition_list_item_t* item = (partition_list_item_t*) calloc(sizeof(partition_list_item_t), 1);
if (item == NULL) {
return ESP_ERR_NO_MEM;
}
item->info.flash_chip = flash_chip;
item->info.address = offset;
item->info.size = size;
item->info.type = type;
item->info.subtype = subtype;
item->info.encrypted = false;
item->user_registered = true;
strlcpy(item->info.label, label, sizeof(item->info.label));
_lock_acquire(&s_partition_list_lock);
partition_list_item_t *it, *last = NULL;
SLIST_FOREACH(it, &s_partition_list, next) {
/* Check if the new partition overlaps an existing one */
if (it->info.flash_chip == flash_chip &&
bootloader_util_regions_overlap(offset, offset + size,
it->info.address, it->info.address + it->info.size)) {
_lock_release(&s_partition_list_lock);
free(item);
return ESP_ERR_INVALID_ARG;
}
last = it;
}
if (last == NULL) {
SLIST_INSERT_HEAD(&s_partition_list, item, next);
} else {
SLIST_INSERT_AFTER(last, item, next);
}
_lock_release(&s_partition_list_lock);
if (out_partition != NULL) {
*out_partition = &item->info;
}
return ESP_OK;
}
esp_err_t esp_partition_deregister_external(const esp_partition_t* partition)
{
esp_err_t result = ESP_ERR_NOT_FOUND;
_lock_acquire(&s_partition_list_lock);
partition_list_item_t *it;
SLIST_FOREACH(it, &s_partition_list, next) {
if (&it->info == partition) {
if (!it->user_registered) {
result = ESP_ERR_INVALID_ARG;
break;
}
SLIST_REMOVE(&s_partition_list, it, partition_list_item_, next);
free(it);
result = ESP_OK;
break;
}
}
_lock_release(&s_partition_list_lock);
return result;
}
const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
{
assert(partition != NULL);
@@ -217,7 +302,8 @@ const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
while (it != NULL) {
const esp_partition_t *p = esp_partition_get(it);
/* Can't memcmp() whole structure here as padding contents may be different */
if (p->address == partition->address
if (p->flash_chip == partition->flash_chip
&& p->address == partition->address
&& partition->size == p->size
&& partition->encrypted == p->encrypted) {
esp_partition_iterator_release(it);
@@ -241,9 +327,17 @@ esp_err_t esp_partition_read(const esp_partition_t* partition,
}
if (!partition->encrypted) {
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size);
#else
return spi_flash_read(partition->address + src_offset, dst, size);
#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL
} else {
#if CONFIG_SECURE_FLASH_ENC_ENABLED
if (partition->flash_chip != esp_flash_default_chip) {
return ESP_ERR_NOT_SUPPORTED;
}
/* Encrypted partitions need to be read via a cache mapping */
const void *buf;
spi_flash_mmap_handle_t handle;
@@ -275,9 +369,16 @@ esp_err_t esp_partition_write(const esp_partition_t* partition,
}
dst_offset = partition->address + dst_offset;
if (!partition->encrypted) {
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
return esp_flash_write(partition->flash_chip, src, dst_offset, size);
#else
return spi_flash_write(dst_offset, src, size);
#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL
} else {
#if CONFIG_SECURE_FLASH_ENC_ENABLED
if (partition->flash_chip != esp_flash_default_chip) {
return ESP_ERR_NOT_SUPPORTED;
}
return spi_flash_write_encrypted(dst_offset, src, size);
#else
return ESP_ERR_NOT_SUPPORTED;
@@ -286,23 +387,26 @@ esp_err_t esp_partition_write(const esp_partition_t* partition,
}
esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
size_t start_addr, size_t size)
size_t offset, size_t size)
{
assert(partition != NULL);
if (start_addr > partition->size) {
if (offset > partition->size) {
return ESP_ERR_INVALID_ARG;
}
if (start_addr + size > partition->size) {
if (offset + size > partition->size) {
return ESP_ERR_INVALID_SIZE;
}
if (size % SPI_FLASH_SEC_SIZE != 0) {
return ESP_ERR_INVALID_SIZE;
}
if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
if (offset % SPI_FLASH_SEC_SIZE != 0) {
return ESP_ERR_INVALID_ARG;
}
return spi_flash_erase_range(partition->address + start_addr, size);
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
return esp_flash_erase_region(partition->flash_chip, partition->address + offset, size);
#else
return spi_flash_erase_range(partition->address + offset, size);
#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL
}
/*
@@ -313,7 +417,7 @@ esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
* we can add esp_partition_mmapv which will accept an array of offsets and sizes, and return array of
* mmaped pointers, and a single handle for all these regions.
*/
esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size,
esp_err_t esp_partition_mmap(const esp_partition_t* partition, size_t offset, size_t size,
spi_flash_mmap_memory_t memory,
const void** out_ptr, spi_flash_mmap_handle_t* out_handle)
{
@@ -324,6 +428,9 @@ esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset,
if (offset + size > partition->size) {
return ESP_ERR_INVALID_SIZE;
}
if (partition->flash_chip != esp_flash_default_chip) {
return ESP_ERR_NOT_SUPPORTED;
}
size_t phys_addr = partition->address + offset;
// offset within 64kB block
size_t region_offset = phys_addr & 0xffff;
@@ -355,3 +462,19 @@ bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_
}
return false;
}
bool esp_partition_main_flash_region_safe(size_t addr, size_t size)
{
bool result = true;
if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) {
return false;
}
const esp_partition_t *p = esp_ota_get_running_partition();
if (addr >= p->address && addr < p->address + p->size) {
return false;
}
if (addr < p->address && addr + size > p->address) {
return false;
}
return result;
}

View File

@@ -0,0 +1,44 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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
/* SPI commands (actual on-wire commands not SPI controller bitmasks)
Suitable for use with spi_flash_hal_common_command static function.
*/
#define CMD_RDID 0x9F
#define CMD_WRSR 0x01
#define SR_WIP (1<<0) /* Status register write-in-progress bit */
#define SR_WREN (1<<1) /* Status register write enable bit */
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
#define CMD_WREN 0x06
#define CMD_WRDI 0x04
#define CMD_RDSR 0x05
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */
#define CMD_FASTRD_QIO 0xEB
#define CMD_FASTRD_QUAD 0x6B
#define CMD_FASTRD_DIO 0xBB
#define CMD_FASTRD_DUAL 0x3B
#define CMD_FASTRD 0x0B
#define CMD_READ 0x03 /* Speed limited */
#define CMD_CHIP_ERASE 0xC7
#define CMD_SECTOR_ERASE 0x20
#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */
#define CMD_RST_EN 0x66
#define CMD_RST_DEV 0x99

View File

@@ -0,0 +1,7 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS CONFIG_SPI_FLASH_DANGEROUS_WRITE
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED

View File

@@ -109,3 +109,5 @@ void *heap_caps_malloc( size_t size, uint32_t caps )
{
return NULL;
}
esp_flash_t* esp_flash_default_chip = NULL;

View File

@@ -0,0 +1,38 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include <stdlib.h>
#include "spi_flash_chip_driver.h"
#include "spi_flash_chip_generic.h"
#include "spi_flash_chip_issi.h"
#include "sdkconfig.h"
/*
* Default registered chip drivers. Note these are tested in order and first
* match is taken, so generic/catchall entries should go last. Note that the
* esp_flash_registered_flash_ops pointer can be changed to point to a different
* array of registered ops, if desired.
*
* It can be configured to support only available chips in the sdkconfig, to
* avoid possible issues, and speed up the auto-detecting.
*/
static const spi_flash_chip_t *default_registered_chips[] = {
#ifdef CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP
&esp_flash_chip_issi,
#endif
&esp_flash_chip_generic,
NULL,
};
const spi_flash_chip_t **esp_flash_registered_chips = default_registered_chips;

View File

@@ -0,0 +1,405 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include <stdlib.h>
#include <sys/param.h> // For MIN/MAX
#include "spi_flash_chip_generic.h"
#include "spi_flash_defs.h"
#include "esp_log.h"
static const char TAG[] = "chip_generic";
#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT 4000
#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT 500
#define SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT 1000
#define DEFAULT_IDLE_TIMEOUT 200
#define DEFAULT_PAGE_PROGRAM_TIMEOUT 500
esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id)
{
// This is the catch-all probe function, claim the chip always if nothing
// else has claimed it yet.
return ESP_OK;
}
esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip)
{
//this is written following the winbond spec..
spi_flash_trans_t t;
t = (spi_flash_trans_t) {
.command = CMD_RST_EN,
};
esp_err_t err = chip->host->common_command(chip->host, &t);
if (err != ESP_OK) {
return err;
}
t = (spi_flash_trans_t) {
.command = CMD_RST_DEV,
};
err = chip->host->common_command(chip->host, &t);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
return err;
}
esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size)
{
uint32_t id = 0;
*size = 0;
esp_err_t err = chip->host->read_id(chip->host, &id);
if (err != ESP_OK) {
return err;
}
/* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or
* 0xC0 or similar. */
if ((id & 0x0F00) != 0) {
return ESP_ERR_FLASH_UNSUPPORTED_CHIP;
}
*size = 1 << (id & 0xFF);
return ESP_OK;
}
esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip)
{
esp_err_t err;
err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == ESP_OK) {
chip->host->erase_chip(chip->host);
//to save time, flush cache here
if (chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, 0, chip->size);
if (err != ESP_OK) {
return err;
}
}
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_address)
{
esp_err_t err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == ESP_OK) {
chip->host->erase_sector(chip->host, start_address);
//to save time, flush cache here
if (chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, start_address, chip->chip_drv->sector_size);
if (err != ESP_OK) {
return err;
}
}
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_address)
{
esp_err_t err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == ESP_OK) {
chip->host->erase_block(chip->host, start_address);
//to save time, flush cache here
if (chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, start_address, chip->chip_drv->block_erase_size);
if (err != ESP_OK) {
return err;
}
}
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
{
esp_err_t err = ESP_OK;
// Configure the host, and return
spi_flash_chip_generic_config_host_read_mode(chip);
while (err == ESP_OK && length > 0) {
uint32_t read_len = MIN(length, chip->host->max_read_bytes);
err = chip->host->read(chip->host, buffer, address, read_len);
buffer += read_len;
length -= read_len;
address += read_len;
}
return err;
}
esp_err_t spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
esp_err_t err;
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
if (err == ESP_OK) {
// Perform the actual Page Program command
chip->host->program_page(chip->host, buffer, address, length);
err = chip->chip_drv->wait_idle(chip, DEFAULT_PAGE_PROGRAM_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
esp_err_t err = ESP_OK;
const uint32_t page_size = chip->chip_drv->page_size;
while (err == ESP_OK && length > 0) {
uint32_t page_len = MIN(chip->host->max_write_bytes, MIN(page_size, length));
if ((address + page_len) / page_size != address / page_size) {
// Most flash chips can't page write across a page boundary
page_len = page_size - (address % page_size);
}
err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->program_page(chip, buffer, address, page_len);
address += page_len;
buffer = (void *)((intptr_t)buffer + page_len);
length -= page_len;
}
}
if (err == ESP_OK && chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, address, length);
}
return err;
}
esp_err_t spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
return ESP_ERR_FLASH_UNSUPPORTED_HOST; // TODO
}
esp_err_t spi_flash_chip_generic_write_enable(esp_flash_t *chip, bool write_protect)
{
esp_err_t err = ESP_OK;
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
if (err == ESP_OK) {
chip->host->set_write_protect(chip->host, write_protect);
}
uint8_t status;
err = chip->host->read_status(chip->host, &status);
if (err != ESP_OK) {
return err;
}
if ((status & SR_WREN) == 0) {
// WREN flag has not been set!
err = ESP_ERR_NOT_FOUND;
}
return err;
}
esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ms)
{
while (chip->host->host_idle(chip->host) && *timeout_ms > 0) {
if (*timeout_ms > 1) {
chip->os_func->delay_ms(chip->os_func_data, 1);
}
(*timeout_ms)--;
}
return (*timeout_ms > 0) ? ESP_OK : ESP_ERR_TIMEOUT;
}
esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_ms)
{
timeout_ms++; // allow at least one pass before timeout, last one has no sleep cycle
uint8_t status = 0;
while (timeout_ms > 0) {
esp_err_t err = spi_flash_generic_wait_host_idle(chip, &timeout_ms);
if (err != ESP_OK) {
return err;
}
err = chip->host->read_status(chip->host, &status);
if (err != ESP_OK) {
return err;
}
if ((status & SR_WIP) == 0) {
break; // Write in progress is complete
}
if (timeout_ms > 1) {
chip->os_func->delay_ms(chip->os_func_data, 1);
}
timeout_ms--;
}
return (timeout_ms > 0) ? ESP_OK : ESP_ERR_TIMEOUT;
}
esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip)
{
uint32_t dummy_cyclelen_base;
uint32_t addr_bitlen;
uint32_t read_command;
switch (chip->read_mode) {
case SPI_FLASH_QIO:
//for QIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that.
addr_bitlen = 32;
dummy_cyclelen_base = 4;
read_command = CMD_FASTRD_QIO;
break;
case SPI_FLASH_QOUT:
addr_bitlen = 24;
dummy_cyclelen_base = 8;
read_command = CMD_FASTRD_QUAD;
break;
case SPI_FLASH_DIO:
//for DIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that.
addr_bitlen = 28;
dummy_cyclelen_base = 2;
read_command = CMD_FASTRD_DIO;
break;
case SPI_FLASH_DOUT:
addr_bitlen = 24;
dummy_cyclelen_base = 8;
read_command = CMD_FASTRD_DUAL;
break;
case SPI_FLASH_FASTRD:
addr_bitlen = 24;
dummy_cyclelen_base = 8;
read_command = CMD_FASTRD;
break;
case SPI_FLASH_SLOWRD:
addr_bitlen = 24;
dummy_cyclelen_base = 0;
read_command = CMD_READ;
break;
default:
return ESP_ERR_FLASH_NOT_INITIALISED;
}
return chip->host->configure_host_read_mode(chip->host, chip->read_mode, addr_bitlen, dummy_cyclelen_base, read_command);
}
esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit)
{
if (spi_flash_is_quad_mode(chip)) {
// Ensure quad modes are enabled, using the Quad Enable parameters supplied.
spi_flash_trans_t t = {
.command = qe_rdsr_command,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = qe_sr_bitwidth,
};
chip->host->common_command(chip->host, &t);
unsigned sr = t.miso_data[0];
ESP_EARLY_LOGV(TAG, "set_read_mode: status before 0x%x", sr);
if ((sr & qe_sr_bit) == 0) {
//some chips needs the write protect to be disabled before writing to Status Register
chip->chip_drv->set_write_protect(chip, false);
sr |= qe_sr_bit;
spi_flash_trans_t t = {
.command = qe_wrsr_command,
.mosi_data = sr,
.mosi_len = qe_sr_bitwidth,
.miso_len = 0,
};
chip->host->common_command(chip->host, &t);
/* Check the new QE bit has stayed set */
spi_flash_trans_t t_rdsr = {
.command = qe_rdsr_command,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = qe_sr_bitwidth
};
chip->host->common_command(chip->host, &t_rdsr);
sr = t_rdsr.miso_data[0];
ESP_EARLY_LOGV(TAG, "set_read_mode: status after 0x%x", sr);
if ((sr & qe_sr_bit) == 0) {
return ESP_ERR_FLASH_NO_RESPONSE;
}
chip->chip_drv->set_write_protect(chip, true);
}
}
return ESP_OK;
}
esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip)
{
// On "generic" chips, this involves checking
// bit 1 (QE) of RDSR2 (35h) result
// (it works this way on GigaDevice & Fudan Micro chips, probably others...)
const uint8_t BIT_QE = 1 << 1;
return spi_flash_common_set_read_mode(chip, CMD_RDSR2, CMD_WRSR2, 8, BIT_QE);
}
static const char chip_name[] = "generic";
const spi_flash_chip_t esp_flash_chip_generic = {
.name = chip_name,
.probe = spi_flash_chip_generic_probe,
.reset = spi_flash_chip_generic_reset,
.detect_size = spi_flash_chip_generic_detect_size,
.erase_chip = spi_flash_chip_generic_erase_chip,
.erase_sector = spi_flash_chip_generic_erase_sector,
.erase_block = spi_flash_chip_generic_erase_block,
.sector_size = 4 * 1024,
.block_erase_size = 64 * 1024,
// TODO: figure out if generic chip-wide protection bits exist across some manufacturers
.get_chip_write_protect = NULL,
.set_chip_write_protect = NULL,
// Chip write protection regions do not appear to be standardised
// at all, this is implemented in chip-specific drivers only.
.num_protectable_regions = 0,
.protectable_regions = NULL,
.get_protected_regions = NULL,
.set_protected_regions = NULL,
.read = spi_flash_chip_generic_read,
.write = spi_flash_chip_generic_write,
.program_page = spi_flash_chip_generic_page_program,
.page_size = 256,
.write_encrypted = spi_flash_chip_generic_write_encrypted,
.set_write_protect = spi_flash_chip_generic_write_enable,
.wait_idle = spi_flash_chip_generic_wait_idle,
.set_read_mode = spi_flash_chip_generic_set_read_mode,
};

View File

@@ -0,0 +1,79 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include <stdlib.h>
#include "spi_flash_chip_generic.h"
#include "spi_flash_defs.h"
/* Driver for ISSI flash chip, as used in ESP32 D2WD */
esp_err_t spi_flash_chip_issi_probe(esp_flash_t *chip, uint32_t flash_id)
{
/* Check manufacturer and product IDs match our desired masks */
const uint8_t MFG_ID = 0x9D;
if (flash_id >> 16 != MFG_ID) {
return ESP_ERR_NOT_FOUND;
}
const uint16_t FLASH_ID_MASK = 0xCF00;
const uint16_t FLASH_ID_VALUE = 0x4000;
if ((flash_id & FLASH_ID_MASK) != FLASH_ID_VALUE) {
return ESP_ERR_NOT_FOUND;
}
return ESP_OK;
}
esp_err_t spi_flash_chip_issi_set_read_mode(esp_flash_t *chip)
{
/* ISSI uses bit 6 of "basic" SR as Quad Enable */
const uint8_t BIT_QE = 1 << 6;
return spi_flash_common_set_read_mode(chip, CMD_RDSR, CMD_WRSR, 8, BIT_QE);
}
static const char chip_name[] = "issi";
// The issi chip can use the functions for generic chips except from set read mode and probe,
// So we only replace these two functions.
const spi_flash_chip_t esp_flash_chip_issi = {
.name = chip_name,
.probe = spi_flash_chip_issi_probe,
.reset = spi_flash_chip_generic_reset,
.detect_size = spi_flash_chip_generic_detect_size,
.erase_chip = spi_flash_chip_generic_erase_chip,
.erase_sector = spi_flash_chip_generic_erase_sector,
.erase_block = spi_flash_chip_generic_erase_block,
.sector_size = 4 * 1024,
.block_erase_size = 64 * 1024,
// TODO: support get/set chip write protect for ISSI flash
.get_chip_write_protect = NULL,
.set_chip_write_protect = NULL,
// TODO support protected regions on ISSI flash
.num_protectable_regions = 0,
.protectable_regions = NULL,
.get_protected_regions = NULL,
.set_protected_regions = NULL,
.read = spi_flash_chip_generic_read,
.write = spi_flash_chip_generic_write,
.program_page = spi_flash_chip_generic_page_program,
.page_size = 256,
.write_encrypted = spi_flash_chip_generic_write_encrypted,
.set_write_protect = spi_flash_chip_generic_write_enable,
.wait_idle = spi_flash_chip_generic_wait_idle,
.set_read_mode = spi_flash_chip_issi_set_read_mode,
};

View File

@@ -0,0 +1,120 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include <stdarg.h>
#include "esp32/rom/ets_sys.h"
#include "esp_attr.h"
#include "esp_spi_flash.h" //for ``g_flash_guard_default_ops``
#include "esp_flash.h"
/*
* OS functions providing delay service and arbitration among chips, and with the cache.
*
* The cache needs to be disabled when chips on the SPI1 bus is under operation, hence these functions need to be put
* into the IRAM,and their data should be put into the DRAM.
*/
typedef struct {
int host_id;
} app_func_arg_t;
// in the future we will have arbitration among devices, including flash on the same flash bus
static IRAM_ATTR esp_err_t spi_bus_acquire(int host_id)
{
return ESP_OK;
}
static IRAM_ATTR esp_err_t spi_bus_release(int host_id)
{
return ESP_OK;
}
//for SPI1, we have to disable the cache and interrupts before using the SPI bus
static IRAM_ATTR esp_err_t spi1_start(void *arg)
{
g_flash_guard_default_ops.start();
spi_bus_acquire(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static IRAM_ATTR esp_err_t spi1_end(void *arg)
{
g_flash_guard_default_ops.end();
spi_bus_release(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static esp_err_t spi23_start(void *arg)
{
spi_bus_acquire(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static esp_err_t spi23_end(void *arg)
{
spi_bus_release(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static IRAM_ATTR esp_err_t delay_ms(void *arg, unsigned ms)
{
ets_delay_us(1000 * ms);
return ESP_OK;
}
static DRAM_ATTR app_func_arg_t spi1_arg = {
.host_id = 0, //for SPI1,
};
static app_func_arg_t spi2_arg = {
.host_id = 1, //for SPI2,
};
static app_func_arg_t spi3_arg = {
.host_id = 2, //for SPI3,
};
//for SPI1, we have to disable the cache and interrupts before using the SPI bus
const DRAM_ATTR esp_flash_os_functions_t esp_flash_spi1_default_os_functions = {
.start = spi1_start,
.end = spi1_end,
.delay_ms = delay_ms,
};
const esp_flash_os_functions_t esp_flash_spi23_default_os_functions = {
.start = spi23_start,
.end = spi23_end,
.delay_ms = delay_ms,
};
esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id)
{
if (host_id == 0) {
//SPI1
chip->os_func = &esp_flash_spi1_default_os_functions;
chip->os_func_data = &spi1_arg;
} else if (host_id == 1 || host_id == 2) {
//SPI2,3
chip->os_func = &esp_flash_spi23_default_os_functions;
chip->os_func_data = (host_id == 1) ? &spi2_arg : &spi3_arg;
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}

View File

@@ -0,0 +1,48 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include <stdarg.h>
#include "esp_flash.h"
#include "esp32/rom/ets_sys.h"
#include "esp32/rom/cache.h"
#include "esp_attr.h"
static esp_err_t start(void *arg)
{
Cache_Read_Disable(0);
Cache_Read_Disable(1);
return ESP_OK;
}
static esp_err_t end(void *arg)
{
Cache_Flush(0);
Cache_Flush(1);
Cache_Read_Enable(0);
Cache_Read_Enable(1);
return ESP_OK;
}
static esp_err_t delay_ms(void *arg, unsigned ms)
{
ets_delay_us(1000 * ms);
return ESP_OK;
}
const esp_flash_os_functions_t esp_flash_noos_functions = {
.start = start,
.end = end,
.delay_ms = delay_ms,
};

View File

@@ -383,6 +383,7 @@ static void spi_cache_mode_switch(uint32_t modebit)
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x6B);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]);
} else if ((modebit & SPI_FREAD_DIO)) {
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_DIO_ADDR_BITSLEN);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_DIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]);
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0xBB);
} else if ((modebit & SPI_FREAD_DUAL)) {

View File

@@ -1,6 +1,3 @@
set(COMPONENT_SRCDIRS ".")
set(COMPONENT_ADD_INCLUDEDIRS ".")
set(COMPONENT_REQUIRES unity test_utils spi_flash bootloader_support app_update)
register_component()
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES unity test_utils spi_flash bootloader_support app_update)

View File

@@ -30,7 +30,7 @@ static IRAM_ATTR void cache_test_task(void *arg)
vTaskDelete(NULL);
}
TEST_CASE("spi_flash_cache_enabled() works on both CPUs", "[spi_flash]")
TEST_CASE("spi_flash_cache_enabled() works on both CPUs", "[spi_flash][esp_flash]")
{
result_queue = xQueueCreate(1, sizeof(bool));

View File

@@ -0,0 +1,495 @@
#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <unity.h>
#include "esp_flash.h"
#include "driver/spi_common.h"
#include "esp_flash_spi_init.h"
#include <esp_attr.h>
#include "esp_log.h"
#include <test_utils.h>
#include "unity.h"
#include "driver/spi_common.h"
#include "driver/gpio.h"
#include "soc/io_mux_reg.h"
#define FUNC_SPI 1
static uint8_t sector_buf[4096];
// #define TEST_SPI1_CS1
// #define TEST_SPI2_CS0
// #define TEST_SPI3_CS0
#define TEST_SPI_SPEED ESP_FLASH_10MHZ
#define TEST_SPI_READ_MODE SPI_FLASH_FASTRD
//#define FORCE_GPIO_MATRIX
#define HSPI_PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI
#define HSPI_PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO
#define HSPI_PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK
#define HSPI_PIN_NUM_HD HSPI_IOMUX_PIN_NUM_HD
#define HSPI_PIN_NUM_WP HSPI_IOMUX_PIN_NUM_WP
#define VSPI_PIN_NUM_MOSI VSPI_IOMUX_PIN_NUM_MOSI
#define VSPI_PIN_NUM_MISO VSPI_IOMUX_PIN_NUM_MISO
#define VSPI_PIN_NUM_CLK VSPI_IOMUX_PIN_NUM_CLK
#define VSPI_PIN_NUM_HD VSPI_IOMUX_PIN_NUM_HD
#define VSPI_PIN_NUM_WP VSPI_IOMUX_PIN_NUM_WP
#if defined TEST_SPI1_CS1
# define TEST_HOST SPI_HOST
# define TEST_CS 1
// #define TEST_CS_PIN 14
# define TEST_CS_PIN 16 //the pin which is usually used by the PSRAM
// #define TEST_CS_PIN 27
# define TEST_INPUT_DELAY 0
# define EXTRA_SPI1_CLK_IO 17 //the pin which is usually used by the PSRAM clk
#elif defined TEST_SPI2_CS0
# define TEST_HOST HSPI_HOST
# define TEST_CS 0
# define TEST_CS_PIN HSPI_IOMUX_PIN_NUM_CS
# define TEST_INPUT_DELAY 20
#elif defined TEST_SPI3_CS0
# define TEST_HOST VSPI_HOST
# define TEST_CS 0
# define TEST_CS_PIN VSPI_IOMUX_PIN_NUM_CS
# define TEST_INPUT_DELAY 0
#else
# define SKIP_EXTENDED_CHIP_TEST
#endif
static const char TAG[] = "test_esp_flash";
#ifndef SKIP_EXTENDED_CHIP_TEST
static esp_flash_t *test_chip = NULL;
static void setup_bus(spi_host_device_t host_id)
{
if (host_id == SPI_HOST) {
ESP_LOGI(TAG, "setup flash on SPI1 CS1...\n");
//no need to initialize the bus, however the CLK may need one more output if it's on the usual place of PSRAM
#ifdef EXTRA_SPI1_CLK_IO
gpio_matrix_out(EXTRA_SPI1_CLK_IO, SPICLK_OUT_IDX, 0, 0);
#endif
//currently the SPI bus for main flash chip is initialized through GPIO matrix
} else if (host_id == HSPI_HOST) {
ESP_LOGI(TAG, "setup flash on SPI2 (HSPI) CS0...\n");
spi_bus_config_t hspi_bus_cfg = {
.mosi_io_num = HSPI_PIN_NUM_MOSI,
.miso_io_num = HSPI_PIN_NUM_MISO,
.sclk_io_num = HSPI_PIN_NUM_CLK,
.quadhd_io_num = HSPI_PIN_NUM_HD,
.quadwp_io_num = HSPI_PIN_NUM_WP,
.max_transfer_sz = 64,
};
#ifdef FORCE_GPIO_MATRIX
hspi_bus_cfg.quadhd_io_num = 23;
#endif
esp_err_t ret = spi_bus_initialize(host_id, &hspi_bus_cfg, 0);
TEST_ESP_OK(ret);
} else if (host_id == VSPI_HOST) {
ESP_LOGI(TAG, "setup flash on SPI3 (VSPI) CS0...\n");
spi_bus_config_t vspi_bus_cfg = {
.mosi_io_num = VSPI_PIN_NUM_MOSI,
.miso_io_num = VSPI_PIN_NUM_MISO,
.sclk_io_num = VSPI_PIN_NUM_CLK,
.quadhd_io_num = VSPI_PIN_NUM_HD,
.quadwp_io_num = VSPI_PIN_NUM_WP,
.max_transfer_sz = 64,
};
#ifdef FORCE_GPIO_MATRIX
vspi_bus_cfg.quadhd_io_num = 23;
#endif
esp_err_t ret = spi_bus_initialize(host_id, &vspi_bus_cfg, 0);
TEST_ESP_OK(ret);
} else {
ESP_LOGE(TAG, "invalid bus");
}
}
static void release_bus(int host_id)
{
if (host_id == HSPI_HOST || host_id == VSPI_HOST) {
spi_bus_free(host_id);
}
}
static void setup_new_chip(esp_flash_read_mode_t io_mode, esp_flash_speed_t speed)
{
//the bus should be initialized before the flash is attached to the bus
setup_bus(TEST_HOST);
esp_flash_spi_device_config_t dev_cfg = {
.host_id = TEST_HOST,
.io_mode = io_mode,
.speed = speed,
.cs_id = TEST_CS,
.cs_io_num = TEST_CS_PIN,
.input_delay_ns = TEST_INPUT_DELAY,
};
esp_err_t err = spi_bus_add_flash_device(&test_chip, &dev_cfg);
TEST_ESP_OK(err);
err = esp_flash_init(test_chip);
TEST_ESP_OK(err);
}
void teardown_test_chip()
{
spi_bus_remove_flash_device(test_chip);
test_chip = NULL;
release_bus(TEST_HOST);
}
#endif
static void test_metadata(esp_flash_t *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t id, size;
TEST_ESP_OK(esp_flash_read_id(chip, &id));
TEST_ESP_OK(esp_flash_get_size(chip, &size));
printf("Flash ID %08x detected size %d bytes\n", id, size);
}
TEST_CASE("SPI flash metadata functions", "[esp_flash]")
{
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_metadata(test_chip);
teardown_test_chip();
#endif
test_metadata(NULL);
}
static uint32_t erase_test_region(esp_flash_t *chip, int num_sectors)
{
const esp_partition_t *part = get_test_data_partition();
uint32_t offs = part->address;
/* chip should be initialised */
TEST_ASSERT(esp_flash_default_chip != NULL
&& esp_flash_chip_driver_initialized(esp_flash_default_chip));
TEST_ASSERT(num_sectors * 4096 <= part->size);
bzero(sector_buf, sizeof(sector_buf));
printf("Erase @ 0x%x...\n", offs);
TEST_ASSERT_EQUAL_HEX32(ESP_OK, esp_flash_erase_region(chip, offs, num_sectors * 4096) );
printf("Verify erased...\n");
for (int i = 0; i < num_sectors; i++) {
TEST_ASSERT_EQUAL_HEX32(ESP_OK, esp_flash_read(chip, sector_buf, offs + i * 4096, sizeof(sector_buf)));
printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]);
for (int i = 0; i < sizeof(sector_buf); i++) {
TEST_ASSERT_EQUAL_HEX8(0xFF, sector_buf[i]);
}
}
return offs;
}
void test_simple_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 1);
const int test_seed = 778;
srand(test_seed);
for (int i = 0 ; i < sizeof(sector_buf); i++) {
sector_buf[i] = rand();
}
printf("Write %p...\n", (void *)offs);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, sector_buf, offs, sizeof(sector_buf)) );
bzero(sector_buf, sizeof(sector_buf));
printf("Read back...\n");
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, sector_buf, offs, sizeof(sector_buf)) );
printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]);
srand(test_seed);
for (int i = 0; i < sizeof(sector_buf); i++) {
TEST_ASSERT_EQUAL_HEX8(rand() & 0xFF, sector_buf[i]);
}
}
TEST_CASE("SPI flash simple read/write", "[esp_flash]")
{
test_simple_read_write(NULL);
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_simple_read_write(test_chip);
teardown_test_chip();
#endif
}
void test_unaligned_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 2);
const char *msg = "i am a message";
TEST_ASSERT(strlen(msg) + 1 % 4 != 0);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, msg, offs + 1, strlen(msg) + 1) );
char buf[strlen(msg) + 1];
memset(buf, 0xEE, sizeof(buf));
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, buf, offs + 1, strlen(msg) + 1) );
TEST_ASSERT_EQUAL_STRING_LEN(msg, buf, strlen(msg));
TEST_ASSERT(memcmp(buf, msg, strlen(msg) + 1) == 0);
}
TEST_CASE("SPI flash unaligned read/write", "[esp_flash]")
{
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_unaligned_read_write(test_chip);
teardown_test_chip();
#endif
test_unaligned_read_write(NULL);
}
void test_single_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 2);
for (unsigned v = 0; v < 512; v++) {
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_flash_write(chip, &v, offs + v, 1) );
}
for (unsigned v = 0; v < 512; v++) {
uint8_t readback;
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_flash_read(chip, &readback, offs + v, 1) );
TEST_ASSERT_EQUAL_HEX8(v, readback);
}
}
TEST_CASE("SPI flash single byte reads/writes", "[esp_flash]")
{
test_single_read_write(NULL);
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_single_read_write(test_chip);
teardown_test_chip();
#endif
}
/* this test is notable because it generates a lot of unaligned reads/writes,
and also reads/writes across both a sector boundary & many page boundaries.
*/
void test_three_byte_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 2);
ets_printf("offs:%X\n", offs);
for (uint32_t v = 0; v < 2000; v++) {
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, &v, offs + 3 * v, 3) );
}
for (uint32_t v = 0; v < 2000; v++) {
uint32_t readback;
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, offs + 3 * v, 3) );
TEST_ASSERT_EQUAL_HEX32(v & 0xFFFFFF, readback & 0xFFFFFF);
}
}
TEST_CASE("SPI flash three byte reads/writes", "[esp_flash]")
{
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_three_byte_read_write(test_chip);
teardown_test_chip();
#endif
test_three_byte_read_write(NULL);
}
void test_erase_large_region(esp_flash_t *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
const esp_partition_t *part = get_test_data_partition();
/* Write some noise at the start and the end of the region */
const char *ohai = "OHAI";
uint32_t readback;
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, ohai, part->address, 5));
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, ohai, part->address + part->size - 5, 5));
/* sanity check what we just wrote. since the partition may haven't been erased, we only check the part which is written to 0. */
uint32_t written_data = *((const uint32_t *)ohai);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address + part->size - 5, 4));
TEST_ASSERT_EQUAL_HEX32(0, readback & (~written_data));
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address, 4));
TEST_ASSERT_EQUAL_HEX32(0, readback & (~written_data));
/* Erase whole region */
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_erase_region(chip, part->address, part->size));
/* ensure both areas we wrote are now all-FFs */
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address, 4));
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address + part->size - 5, 4));
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback);
}
TEST_CASE("SPI flash erase large region", "[esp_flash]")
{
test_erase_large_region(NULL);
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_erase_large_region(test_chip);
teardown_test_chip();
#endif
}
static const uint8_t large_const_buffer[16400] = {
203, // first byte
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
[50 ... 99] = 2,
[1600 ... 2000] = 3,
[8000 ... 9000] = 77,
[15000 ... 16398] = 8,
43 // last byte
};
static void test_write_large_buffer(esp_flash_t *chip, const uint8_t *source, size_t length);
static void write_large_buffer(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length);
static void read_and_check(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length);
TEST_CASE("SPI flash test reading with all speed/mode permutations", "[esp_flash]")
{
const int length = sizeof(large_const_buffer);
uint8_t *source_buf = malloc(length);
TEST_ASSERT_NOT_NULL(source_buf);
srand(778);
for (int i = 0; i < length; i++) {
source_buf[i] = rand();
}
const esp_partition_t *part = get_test_data_partition();
TEST_ASSERT(part->size > length + 2 + SPI_FLASH_SEC_SIZE);
#ifndef SKIP_EXTENDED_CHIP_TEST
//use the lowest speed to write and read to make sure success
setup_new_chip(TEST_SPI_READ_MODE, ESP_FLASH_SPEED_MIN);
write_large_buffer(test_chip, part, source_buf, length);
read_and_check(test_chip, part, source_buf, length);
teardown_test_chip();
esp_flash_read_mode_t io_mode = SPI_FLASH_READ_MODE_MIN;
while (io_mode != SPI_FLASH_READ_MODE_MAX) {
esp_flash_speed_t speed = ESP_FLASH_SPEED_MIN;
while (speed != ESP_FLASH_SPEED_MAX) {
ESP_LOGI(TAG, "test flash io mode: %d, speed: %d", io_mode, speed);
setup_new_chip(io_mode, speed);
read_and_check(test_chip, part, source_buf, length);
teardown_test_chip();
speed++;
}
io_mode++;
}
#endif
//test main flash BTW
write_large_buffer(NULL, part, source_buf, length);
read_and_check(NULL, part, source_buf, length);
free(source_buf);
}
TEST_CASE("Test esp_flash_write large const buffer", "[esp_flash]")
{
//buffer in flash
test_write_large_buffer(NULL, large_const_buffer, sizeof(large_const_buffer));
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_write_large_buffer(test_chip, large_const_buffer, sizeof(large_const_buffer));
teardown_test_chip();
#endif
}
#ifndef SKIP_EXTENDED_CHIP_TEST
TEST_CASE("Test esp_flash_write large RAM buffer", "[esp_flash]")
{
// buffer in RAM
uint8_t *source_buf = malloc(sizeof(large_const_buffer));
TEST_ASSERT_NOT_NULL(source_buf);
memcpy(source_buf, large_const_buffer, sizeof(large_const_buffer));
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_write_large_buffer(test_chip, source_buf, sizeof(large_const_buffer));
teardown_test_chip();
free(source_buf);
}
#endif
static void write_large_buffer(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length)
{
printf("Writing chip %p, %d bytes from source %p\n", chip, length, source);
ESP_ERROR_CHECK( esp_flash_erase_region(chip, part->address, (length + SPI_FLASH_SEC_SIZE) & ~(SPI_FLASH_SEC_SIZE - 1)) );
// note writing to unaligned address
ESP_ERROR_CHECK( esp_flash_write(chip, source, part->address + 1, length) );
}
static void read_and_check(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length)
{
printf("Checking chip %p, %d bytes\n", chip, length);
uint8_t *buf = malloc(length);
TEST_ASSERT_NOT_NULL(buf);
ESP_ERROR_CHECK( esp_flash_read(chip, buf, part->address + 1, length) );
TEST_ASSERT_EQUAL_HEX8_ARRAY(source, buf, length);
free(buf);
// check nothing was written at beginning or end
uint8_t ends[8];
ESP_ERROR_CHECK( esp_flash_read(chip, ends, part->address, sizeof(ends)) );
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[0]);
TEST_ASSERT_EQUAL_HEX8(source[0], ends[1]);
ESP_ERROR_CHECK( esp_flash_read(chip, ends, part->address + length, sizeof(ends)) );
TEST_ASSERT_EQUAL_HEX8(source[length - 1], ends[0]);
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[1]);
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[2]);
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[3]);
}
static void test_write_large_buffer(esp_flash_t *chip, const uint8_t *source, size_t length)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
const esp_partition_t *part = get_test_data_partition();
TEST_ASSERT(part->size > length + 2 + SPI_FLASH_SEC_SIZE);
write_large_buffer(chip, part, source, length);
read_and_check(chip, part, source, length);
}

View File

@@ -42,13 +42,13 @@ static const uint8_t large_const_buffer[16400] = {
static void test_write_large_buffer(const uint8_t *source, size_t length);
TEST_CASE("Test spi_flash_write large const buffer", "[spi_flash]")
TEST_CASE("Test spi_flash_write large const buffer", "[spi_flash][esp_flash]")
{
// buffer in flash
test_write_large_buffer(large_const_buffer, sizeof(large_const_buffer));
}
TEST_CASE("Test spi_flash_write large RAM buffer", "[spi_flash]")
TEST_CASE("Test spi_flash_write large RAM buffer", "[spi_flash][esp_flash]")
{
// buffer in RAM
uint8_t *source_buf = malloc(sizeof(large_const_buffer));

View File

@@ -200,7 +200,7 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash]")
pages[i]=startpage+(nopages-1)-i;
printf("Offset %x page %d\n", i*0x10000, pages[i]);
}
printf("Attempting mapping of unordered pages to contiguous memory area\n");
spi_flash_mmap_handle_t handle1;
@@ -277,7 +277,7 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash]")
TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash]")
{
//this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB
//this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB
setup_mmap_tests();
printf("Mapping %x (+%x)\n", start, end - start);

View File

@@ -4,14 +4,14 @@
#include "esp_spi_flash.h"
#include "esp_ota_ops.h"
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS || CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS || CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS
static const char *data = "blah blah blah";
#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS
#define TEST_TAGS "[spi_flash]"
#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS
#define TEST_TAGS "[spi_flash][esp_flash]"
#else // ABORTS
#define TEST_TAGS "[spi_flash][ignore]"
#define TEST_TAGS "[spi_flash][esp_flash][ignore]"
#endif
TEST_CASE("can't overwrite bootloader", TEST_TAGS)

View File

@@ -0,0 +1,47 @@
#include "esp_flash.h"
#include "esp_partition.h"
#include "unity.h"
TEST_CASE("Basic handling of a partition in external flash", "[partition]")
{
esp_flash_t flash = {
.size = 1 * 1024 * 1024,
};
const esp_partition_type_t t = ESP_PARTITION_TYPE_DATA;
const esp_partition_subtype_t st = ESP_PARTITION_SUBTYPE_DATA_FAT;
const char* label = "ext_fat";
/* can register */
const esp_partition_t* ext_partition;
TEST_ESP_OK(esp_partition_register_external(&flash, 0, flash.size, label, t, st, &ext_partition));
/* can find the registered partition */
const esp_partition_t* found = esp_partition_find_first(t, st, label);
TEST_ASSERT_EQUAL_HEX(ext_partition, found);
TEST_ASSERT_EQUAL(found->size, flash.size);
TEST_ASSERT_EQUAL(found->address, 0);
/* can deregister */
TEST_ESP_OK(esp_partition_deregister_external(ext_partition));
/* can not deregister one of the partitions on the main flash chip */
const esp_partition_t* nvs_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
TEST_ASSERT_NOT_NULL(nvs_partition);
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_partition_deregister_external(nvs_partition));
/* can not deregister an unknown partition */
esp_partition_t dummy_partition = {};
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_partition_deregister_external(&dummy_partition));
/* can not register a partition larger than the flash size */
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE,
esp_partition_register_external(&flash, 0, 2 * flash.size, "ext_fat", t, st, &ext_partition));
/* can not register an overlapping partition */
TEST_ESP_OK(esp_partition_register_external(&flash, 0, 2 * SPI_FLASH_SEC_SIZE, "p1", t, st, &ext_partition));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_partition_register_external(&flash, SPI_FLASH_SEC_SIZE, 2 * SPI_FLASH_SEC_SIZE,
"p2", t, st, NULL));
TEST_ESP_OK(esp_partition_deregister_external(ext_partition));
}

View File

@@ -25,7 +25,7 @@
#include <esp_partition.h>
#include <esp_attr.h>
TEST_CASE("Test erase partition", "[spi_flash]")
TEST_CASE("Test erase partition", "[spi_flash][esp_flash]")
{
const esp_partition_t *part = get_test_data_partition();

View File

@@ -79,7 +79,7 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len)
spi_flash_disable_interrupts_caches_and_other_cpu();
esp_rom_spiflash_result_t rc = esp_rom_spiflash_write(start, src_buf, sizeof(src_buf));
spi_flash_enable_interrupts_caches_and_other_cpu();
TEST_ASSERT_EQUAL_INT(rc, ESP_ROM_SPIFLASH_RESULT_OK);
TEST_ASSERT_EQUAL_HEX(rc, ESP_ROM_SPIFLASH_RESULT_OK);
memset(dst_buf, 0x55, sizeof(dst_buf));
memset(dst_gold, 0x55, sizeof(dst_gold));
fill(dst_gold + dst_off, src_off, len);
@@ -87,7 +87,7 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len)
TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0);
}
TEST_CASE("Test spi_flash_read", "[spi_flash]")
TEST_CASE("Test spi_flash_read", "[spi_flash][esp_flash]")
{
setup_tests();
#if CONFIG_SPI_FLASH_MINIMAL_TEST
@@ -158,14 +158,16 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len)
fill(dst_gold + dst_off, src_off, len);
}
ESP_ERROR_CHECK(spi_flash_write(start + dst_off, src_buf + src_off, len));
spi_flash_disable_interrupts_caches_and_other_cpu();
esp_rom_spiflash_result_t rc = esp_rom_spiflash_read(start, dst_buf, sizeof(dst_buf));
spi_flash_enable_interrupts_caches_and_other_cpu();
TEST_ASSERT_EQUAL_INT(rc, ESP_ROM_SPIFLASH_RESULT_OK);
TEST_ASSERT_EQUAL_HEX(rc, ESP_ROM_SPIFLASH_RESULT_OK);
TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0);
}
TEST_CASE("Test spi_flash_write", "[spi_flash]")
TEST_CASE("Test spi_flash_write", "[spi_flash][esp_flash]")
{
setup_tests();
#if CONFIG_SPI_FLASH_MINIMAL_TEST

View File

@@ -128,7 +128,7 @@ static void read_task(void* varg) {
vTaskDelete(NULL);
}
TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash]")
TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash][esp_flash]")
{
const size_t size = 128;
read_task_arg_t read_arg = {
@@ -174,7 +174,7 @@ TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash]
#if portNUM_PROCESSORS > 1
TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash]")
TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash][esp_flash]")
{
typedef struct {
QueueHandle_t queue;