kopia lustrzana https://github.com/espressif/esp-idf
refactor(spi): moved spi hw sharing func to hw support
Common spi functionality for sharing the SPI bus between modules is moved from esp_driver_spi to a more fitting location in esp_hw_support (shared HW resource control). This also allows us to decouple the spi_flash driver from esp_driver_spi, removing esp_driver_spi and esp_ringbuf from G1 builds.pull/12501/merge
rodzic
e7734a3367
commit
52e3f09b32
|
@ -4,7 +4,7 @@ if(${target} STREQUAL "linux")
|
||||||
return() # This component is not supported by the POSIX/Linux simulator
|
return() # This component is not supported by the POSIX/Linux simulator
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(srcs "spi_bus_lock.c")
|
set(srcs "")
|
||||||
|
|
||||||
set(public_include "include")
|
set(public_include "include")
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ menu "ESP-Driver:SPI Configurations"
|
||||||
default n
|
default n
|
||||||
depends on !FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
depends on !FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||||
select SPI_MASTER_ISR_IN_IRAM
|
select SPI_MASTER_ISR_IN_IRAM
|
||||||
|
select ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM
|
||||||
help
|
help
|
||||||
Normally only the ISR of SPI master is placed in the IRAM, so that it
|
Normally only the ISR of SPI master is placed in the IRAM, so that it
|
||||||
can work without the flash when interrupt is triggered.
|
can work without the flash when interrupt is triggered.
|
||||||
|
@ -24,6 +25,7 @@ menu "ESP-Driver:SPI Configurations"
|
||||||
depends on !HEAP_PLACE_FUNCTION_INTO_FLASH
|
depends on !HEAP_PLACE_FUNCTION_INTO_FLASH
|
||||||
select PERIPH_CTRL_FUNC_IN_IRAM
|
select PERIPH_CTRL_FUNC_IN_IRAM
|
||||||
select HAL_SPI_MASTER_FUNC_IN_IRAM
|
select HAL_SPI_MASTER_FUNC_IN_IRAM
|
||||||
|
select ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM
|
||||||
help
|
help
|
||||||
Place the SPI master ISR in to IRAM to avoid possible cache miss.
|
Place the SPI master ISR in to IRAM to avoid possible cache miss.
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "hal/dma_types.h"
|
#include "hal/dma_types.h"
|
||||||
#include "soc/gdma_channel.h"
|
#include "soc/gdma_channel.h"
|
||||||
#include "esp_pm.h"
|
#include "esp_pm.h"
|
||||||
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
#if SOC_GDMA_SUPPORTED
|
#if SOC_GDMA_SUPPORTED
|
||||||
#include "esp_private/gdma.h"
|
#include "esp_private/gdma.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,14 +37,6 @@ extern "C"
|
||||||
#define SPI_MASTER_ATTR
|
#define SPI_MASTER_ATTR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BUS_LOCK_DEBUG 0
|
|
||||||
|
|
||||||
#if BUS_LOCK_DEBUG
|
|
||||||
#define BUS_LOCK_DEBUG_EXECUTE_CHECK(x) assert(x)
|
|
||||||
#else
|
|
||||||
#define BUS_LOCK_DEBUG_EXECUTE_CHECK(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB
|
#if SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB
|
||||||
#define DMA_DESC_MEM_ALIGN_SIZE 4
|
#define DMA_DESC_MEM_ALIGN_SIZE 4
|
||||||
#define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel
|
#define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel
|
||||||
|
@ -54,16 +47,6 @@ typedef dma_descriptor_align4_t spi_dma_desc_t;
|
||||||
typedef dma_descriptor_align8_t spi_dma_desc_t;
|
typedef dma_descriptor_align8_t spi_dma_desc_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct spi_bus_lock_t;
|
|
||||||
struct spi_bus_lock_dev_t;
|
|
||||||
/// Handle to the lock of an SPI bus
|
|
||||||
typedef struct spi_bus_lock_t* spi_bus_lock_handle_t;
|
|
||||||
/// Handle to lock of one of the device on an SPI bus
|
|
||||||
typedef struct spi_bus_lock_dev_t* spi_bus_lock_dev_handle_t;
|
|
||||||
|
|
||||||
/// Background operation control function
|
|
||||||
typedef void (*bg_ctrl_func_t)(void*);
|
|
||||||
|
|
||||||
/// Attributes of an SPI bus
|
/// Attributes of an SPI bus
|
||||||
typedef struct {
|
typedef struct {
|
||||||
spi_bus_config_t bus_cfg; ///< Config used to initialize the bus
|
spi_bus_config_t bus_cfg; ///< Config used to initialize the bus
|
||||||
|
@ -85,36 +68,6 @@ typedef struct {
|
||||||
/// Destructor called when a bus is deinitialized.
|
/// Destructor called when a bus is deinitialized.
|
||||||
typedef esp_err_t (*spi_destroy_func_t)(void*);
|
typedef esp_err_t (*spi_destroy_func_t)(void*);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Try to claim a SPI peripheral
|
|
||||||
*
|
|
||||||
* Call this if your driver wants to manage a SPI peripheral.
|
|
||||||
*
|
|
||||||
* @param host Peripheral to claim
|
|
||||||
* @param source The caller indentification string.
|
|
||||||
*
|
|
||||||
* @return True if peripheral is claimed successfully; false if peripheral already is claimed.
|
|
||||||
*/
|
|
||||||
bool spicommon_periph_claim(spi_host_device_t host, const char* source);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check whether the spi periph is in use.
|
|
||||||
*
|
|
||||||
* @param host Peripheral to check.
|
|
||||||
*
|
|
||||||
* @return True if in use, otherwise false.
|
|
||||||
*/
|
|
||||||
bool spicommon_periph_in_use(spi_host_device_t host);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Return the SPI peripheral so another driver can claim it.
|
|
||||||
*
|
|
||||||
* @param host Peripheral to return
|
|
||||||
*
|
|
||||||
* @return True if peripheral is returned successfully; false if peripheral was free to claim already.
|
|
||||||
*/
|
|
||||||
bool spicommon_periph_free(spi_host_device_t host);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Alloc DMA for SPI
|
* @brief Alloc DMA for SPI
|
||||||
*
|
*
|
||||||
|
@ -330,449 +283,6 @@ const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id);
|
||||||
esp_err_t spi_bus_register_destroy_func(spi_host_device_t host_id,
|
esp_err_t spi_bus_register_destroy_func(spi_host_device_t host_id,
|
||||||
spi_destroy_func_t f, void *arg);
|
spi_destroy_func_t f, void *arg);
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* SPI Bus Lock for arbitration among SPI master (intr, polling) trans, SPI flash operations and
|
|
||||||
* flash/psram cache access.
|
|
||||||
*
|
|
||||||
* NON-PUBLIC API. Don't use it directly in applications.
|
|
||||||
*
|
|
||||||
* There is the main lock corresponding to an SPI bus, of which several devices (holding child
|
|
||||||
* locks) attaching to it. Each of the device is STRONGLY RECOMMENDED to be used in only one task
|
|
||||||
* to avoid concurrency issues.
|
|
||||||
*
|
|
||||||
* Terms:
|
|
||||||
* - BG operations (BackGround operations) means some transaction that will not immediately /
|
|
||||||
* explicitly be sent in the task. It can be some cache access, or interrupt transactions.
|
|
||||||
*
|
|
||||||
* - Operation: usage of the bus, for example, do SPI transactions.
|
|
||||||
*
|
|
||||||
* - Acquiring processor: the task or the ISR that is allowed to use the bus. No operations will be
|
|
||||||
* performed if there is no acquiring processor. A processor becomes the acquiring processor if
|
|
||||||
* it ask for that when no acquiring processor exist, otherwise it has to wait for the acquiring
|
|
||||||
* processor to handle over the role to it. The acquiring processor will and will only assign one
|
|
||||||
* acquiring processor in the waiting list (if not empty) when it finishes its operation.
|
|
||||||
*
|
|
||||||
* - Acquiring device: the only device allowed to use the bus. Operations can be performed in
|
|
||||||
* either the BG or the task. When there's no acquiring device, only the ISR is allowed to be the
|
|
||||||
* acquiring processor and perform operations on the bus.
|
|
||||||
*
|
|
||||||
* When a device wants to perform operations, it either:
|
|
||||||
* 1. Acquire the bus, and operate in the task (e.g. polling transactions of SPI master, and SPI flash
|
|
||||||
* operations)
|
|
||||||
*
|
|
||||||
* 2. Request a BG operation. And the ISR will be enabled at proper time.
|
|
||||||
*
|
|
||||||
* For example if a task wants to send an interrupt transaction, it prepares the data in the task,
|
|
||||||
* call `spi_bus_lock_bg_request`, and handle sending in the ISR.
|
|
||||||
*
|
|
||||||
* 3. When a device has already acquired the bus, BG operations are also allowed. After the
|
|
||||||
* `spi_bus_lock_bg_request` is called, call `spi_bus_lock_wait_bg_done` before operations in task
|
|
||||||
* again to wait until BG operations are done.
|
|
||||||
*
|
|
||||||
* Any device may try to invoke the ISR (by `spi_bus_lock_bg_request`). The ISR will be invoked and
|
|
||||||
* become the acquiring processor immediately when the bus is not acquired by other processors. Any
|
|
||||||
* device may also try to acquire the bus (by `spi_bus_lock_acquire_start`). The device will become
|
|
||||||
* the acquiring processor immediately when the bus is not acquired and there is no request active.
|
|
||||||
*
|
|
||||||
* The acquiring processor must be aware of its acquiring role, and properly transfer the acquiring
|
|
||||||
* processor to other tasks or ISR when they have nothing else to do. Before picking a new
|
|
||||||
* acquiring processor, a new acquiring device must be picked first, if there are other devices,
|
|
||||||
* asking to be acquiring device. After that, the new acquiring processor is picked by the sequence
|
|
||||||
* below:
|
|
||||||
*
|
|
||||||
* 1. If there is an acquiring device:
|
|
||||||
* 1.1 The ISR, if acquiring device has active BG requests
|
|
||||||
* 1.2 The task of the device, if no active BG request for the device
|
|
||||||
* 2. The ISR, if there's no acquiring device, but any BG request is active
|
|
||||||
* 3. No one becomes the acquiring processor
|
|
||||||
*
|
|
||||||
* The API also helps on the arbitration of SPI cs lines. The bus is initialized with a cs_num
|
|
||||||
* argument. When attaching devices onto the bus with `spi_bus_lock_register_dev`, it will allocate
|
|
||||||
* devices with different device ID according to the flags given. If the ID is smaller than the
|
|
||||||
* cs_num given when bus is initialized, error will be returned.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* * Initialization:
|
|
||||||
* 1. Call `spi_bus_init_lock` to register a lock for a bus.
|
|
||||||
* 2. Call `spi_bus_lock_set_bg_control` to prepare BG enable/disable functions for
|
|
||||||
* the lock.
|
|
||||||
* 3. Call `spi_bus_lock_register_dev` for each devices that may make use of the
|
|
||||||
* bus, properly store the returned handle, representing those devices.
|
|
||||||
*
|
|
||||||
* * Acquiring:
|
|
||||||
* 1. Call `spi_bus_lock_acquire_start` when a device wants to use the bus
|
|
||||||
* 2. Call `spi_bus_lock_touch` to mark the bus as touched by this device. Also check if the bus
|
|
||||||
* has been touched by other devices.
|
|
||||||
* 3. (optional) Do something on the bus...
|
|
||||||
* 4. (optional) Call `spi_bus_lock_bg_request` to inform and invoke the BG. See ISR below about
|
|
||||||
* ISR operations.
|
|
||||||
* 5. (optional) If `spi_bus_lock_bg_request` is done, you have to call `spi_bus_lock_wait_bg_done`
|
|
||||||
* before touching the bus again, or do the following steps.
|
|
||||||
* 6. Call `spi_bus_lock_acquire_end` to release the bus to other devices.
|
|
||||||
*
|
|
||||||
* * ISR:
|
|
||||||
* 1. Call `spi_bus_lock_bg_entry` when entering the ISR, run or skip the closure for the previous
|
|
||||||
* operation according to the return value.
|
|
||||||
* 2. Call `spi_bus_lock_get_acquiring_dev` to get the acquiring device. If there is no acquiring
|
|
||||||
* device, call `spi_bus_lock_bg_check_dev_acq` to check and update a new acquiring device.
|
|
||||||
* 3. Call `spi_bus_lock_bg_check_dev_req` to check for request of the desired device. If the
|
|
||||||
* desired device is not requested, go to step 5.
|
|
||||||
* 4. Check, start operation for the desired device and go to step 6; otherwise if no operations
|
|
||||||
* can be performed, call `spi_bus_lock_bg_clear_req` to clear the request for this device. If
|
|
||||||
* `spi_bus_lock_bg_clear_req` is called and there is no BG requests active, goto step 6.
|
|
||||||
* 5. (optional) If the device is the acquiring device, go to step 6, otherwise
|
|
||||||
* find another desired device, and go back to step 3.
|
|
||||||
* 6. Call `spi_bus_lock_bg_exit` to try quitting the ISR. If failed, go back to step 2 to look for
|
|
||||||
* a new request again. Otherwise, quit the ISR.
|
|
||||||
*
|
|
||||||
* * Deinitialization (optional):
|
|
||||||
* 1. Call `spi_bus_lock_unregister_dev` for each device when they are no longer needed.
|
|
||||||
* 2. Call `spi_bus_deinit_lock` to release the resources occupied by the lock.
|
|
||||||
*
|
|
||||||
* Some technical details:
|
|
||||||
*
|
|
||||||
* The child-lock of each device will have its own Binary Semaphore, which allows the task serving
|
|
||||||
* this device (task A) being blocked when it fail to become the acquiring processor while it's
|
|
||||||
* calling `spi_bus_lock_acquire_start` or `spi_bus_lock_wait_bg_done`. If it is blocked, there
|
|
||||||
* must be an acquiring processor (either the ISR or another task (task B)), is doing transaction
|
|
||||||
* on the bus. After that, task A will get unblocked and become the acquiring processor when the
|
|
||||||
* ISR call `spi_bus_lock_bg_resume_acquired_dev`, or task B call `spi_bus_lock_acquire_end`.
|
|
||||||
*
|
|
||||||
* When the device wants to send ISR transaction, it should call `spi_bus_lock_bg_request` after
|
|
||||||
* the data is prepared. This function sets a request bit in the critical resource. The ISR will be
|
|
||||||
* invoked and become the new acquiring processor, when:
|
|
||||||
*
|
|
||||||
* 1. A task calls `spi_bus_lock_bg_request` while there is no acquiring processor;
|
|
||||||
* 2. A tasks calls `spi_bus_lock_bg_request` while the task is the acquiring processor. Then the
|
|
||||||
* acquiring processor is handled over to the ISR;
|
|
||||||
* 3. A tasks who is the acquiring processor release the bus by calling `spi_bus_lock_acquire_end`,
|
|
||||||
* and the ISR happens to be the next acquiring processor.
|
|
||||||
*
|
|
||||||
* The ISR will check (by `spi_bus_lock_bg_check_dev_req`) and clear a request bit (by
|
|
||||||
* `spi_bus_lock_bg_clear_req`) after it confirm that all the requests of the corresponding device
|
|
||||||
* are served. The request bit supports being written to recursively, which means, the task don't
|
|
||||||
* need to wait for `spi_bus_lock_bg_clear_req` before call another `spi_bus_lock_bg_request`. The
|
|
||||||
* API will handle the concurrency conflicts properly.
|
|
||||||
*
|
|
||||||
* The `spi_bus_lock_bg_exit` (together with `spi_bus_lock_bg_entry` called before)` is responsible
|
|
||||||
* to ensure ONE and ONLY ONE of the following will happen when the ISR try to give up its
|
|
||||||
* acquiring processor rule:
|
|
||||||
*
|
|
||||||
* 1. ISR quit, no any task unblocked while the interrupt disabled, and none of the BG bits is
|
|
||||||
* active.
|
|
||||||
* 2. ISR quit, there is an acquiring device, and the acquiring processor is passed to the task
|
|
||||||
* serving the acquiring device by unblocking the task.
|
|
||||||
* 3. The ISR failed to quit and have to try again.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#define DEV_NUM_MAX 6 ///< Number of devices supported by this lock
|
|
||||||
|
|
||||||
/// Lock configuration struct
|
|
||||||
typedef struct {
|
|
||||||
int host_id; ///< SPI host id
|
|
||||||
int cs_num; ///< Physical cs numbers of the host
|
|
||||||
} spi_bus_lock_config_t;
|
|
||||||
|
|
||||||
/// Child-lock configuration struct
|
|
||||||
typedef struct {
|
|
||||||
uint32_t flags; ///< flags for the lock, OR-ed of `SPI_BUS_LOCK_DEV_*` flags.
|
|
||||||
#define SPI_BUS_LOCK_DEV_FLAG_CS_REQUIRED BIT(0) ///< The device needs a physical CS pin.
|
|
||||||
} spi_bus_lock_dev_config_t;
|
|
||||||
|
|
||||||
/************* Common *********************/
|
|
||||||
/**
|
|
||||||
* Initialize a lock for an SPI bus.
|
|
||||||
*
|
|
||||||
* @param out_lock Output of the handle to the lock
|
|
||||||
* @return
|
|
||||||
* - ESP_ERR_NO_MEM: if memory exhausted
|
|
||||||
* - ESP_OK: if success
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_init_lock(spi_bus_lock_handle_t *out_lock, const spi_bus_lock_config_t *config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free the resources used by an SPI bus lock.
|
|
||||||
*
|
|
||||||
* @note All attached devices should have been unregistered before calling this
|
|
||||||
* funciton.
|
|
||||||
*
|
|
||||||
* @param lock Handle to the lock to free.
|
|
||||||
*/
|
|
||||||
void spi_bus_deinit_lock(spi_bus_lock_handle_t lock);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the corresponding lock according to bus id.
|
|
||||||
*
|
|
||||||
* @param host_id The bus id to get the lock
|
|
||||||
* @return The lock handle
|
|
||||||
*/
|
|
||||||
spi_bus_lock_handle_t spi_bus_lock_get_by_id(spi_host_device_t host_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Configure how the SPI bus lock enable the background operation.
|
|
||||||
*
|
|
||||||
* @note The lock will not try to stop the background operations, but wait for
|
|
||||||
* The background operations finished indicated by `spi_bus_lock_bg_resume_acquired_dev`.
|
|
||||||
*
|
|
||||||
* @param lock Handle to the lock to set
|
|
||||||
* @param bg_enable The enabling function
|
|
||||||
* @param bg_disable The disabling function, set to NULL if not required
|
|
||||||
* @param arg Argument to pass to the enabling/disabling function.
|
|
||||||
*/
|
|
||||||
void spi_bus_lock_set_bg_control(spi_bus_lock_handle_t lock, bg_ctrl_func_t bg_enable,
|
|
||||||
bg_ctrl_func_t bg_disable, void *arg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach a device onto an SPI bus lock. The returning handle is used to perform
|
|
||||||
* following requests for the attached device.
|
|
||||||
*
|
|
||||||
* @param lock SPI bus lock to attach
|
|
||||||
* @param out_dev_handle Output handle corresponding to the device
|
|
||||||
* @param flags requirement of the device, bitwise OR of SPI_BUS_LOCK_FLAG_* flags
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_ERR_NOT_SUPPORTED: if there's no hardware resources for new devices.
|
|
||||||
* - ESP_ERR_NO_MEM: if memory exhausted
|
|
||||||
* - ESP_OK: if success
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_lock_register_dev(spi_bus_lock_handle_t lock,
|
|
||||||
spi_bus_lock_dev_config_t *config,
|
|
||||||
spi_bus_lock_dev_handle_t *out_dev_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detach a device from its bus and free the resources used
|
|
||||||
*
|
|
||||||
* @param dev_handle Handle to the device.
|
|
||||||
*/
|
|
||||||
void spi_bus_lock_unregister_dev(spi_bus_lock_dev_handle_t dev_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the parent bus lock of the device
|
|
||||||
*
|
|
||||||
* @param dev_handle Handle to the device to get bus lock
|
|
||||||
* @return The bus lock handle
|
|
||||||
*/
|
|
||||||
spi_bus_lock_handle_t spi_bus_lock_get_parent(spi_bus_lock_dev_handle_t dev_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the device ID of a lock.
|
|
||||||
*
|
|
||||||
* The callers should allocate CS pins according to this ID.
|
|
||||||
*
|
|
||||||
* @param dev_handle Handle to the device to get ID
|
|
||||||
* @return ID of the device
|
|
||||||
*/
|
|
||||||
int spi_bus_lock_get_dev_id(spi_bus_lock_dev_handle_t dev_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The device request to touch bus registers. Can only be called by the acquiring processor.
|
|
||||||
*
|
|
||||||
* Also check if the registers has been touched by other devices.
|
|
||||||
*
|
|
||||||
* @param dev_handle Handle to the device to operate the registers
|
|
||||||
* @return true if there has been other devices touching SPI registers.
|
|
||||||
* The caller may need to do a full-configuration. Otherwise return
|
|
||||||
* false.
|
|
||||||
*/
|
|
||||||
bool spi_bus_lock_touch(spi_bus_lock_dev_handle_t dev_handle);
|
|
||||||
|
|
||||||
/************* Acquiring service *********************/
|
|
||||||
/**
|
|
||||||
* Acquiring the SPI bus for exclusive use. Will also wait for the BG to finish all requests of
|
|
||||||
* this device before it returns.
|
|
||||||
*
|
|
||||||
* After successfully return, the caller becomes the acquiring processor.
|
|
||||||
*
|
|
||||||
* @note For the main flash bus, `bg_disable` will be called to disable the cache.
|
|
||||||
*
|
|
||||||
* @param dev_handle Handle to the device request for acquiring.
|
|
||||||
* @param wait Time to wait until timeout or succeed, must be `portMAX_DELAY` for now.
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: on success
|
|
||||||
* - ESP_ERR_INVALID_ARG: timeout is not portMAX_DELAY
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_lock_acquire_start(spi_bus_lock_dev_handle_t dev_handle, TickType_t wait);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release the bus acquired. Will pass the acquiring processor to other blocked
|
|
||||||
* processors (tasks or ISR), and cause them to be unblocked or invoked.
|
|
||||||
*
|
|
||||||
* The acquiring device may also become NULL if no device is asking for acquiring.
|
|
||||||
* In this case, the BG may be invoked if there is any BG requests.
|
|
||||||
*
|
|
||||||
* If the new acquiring device has BG requests, the BG will be invoked before the
|
|
||||||
* task is resumed later after the BG finishes all requests of the new acquiring
|
|
||||||
* device. Otherwise the task of the new acquiring device will be resumed immediately.
|
|
||||||
*
|
|
||||||
* @param dev_handle Handle to the device releasing the bus.
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: on success
|
|
||||||
* - ESP_ERR_INVALID_STATE: the device hasn't acquired the lock yet
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_lock_acquire_end(spi_bus_lock_dev_handle_t dev_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the device acquiring the bus.
|
|
||||||
*
|
|
||||||
* @note Return value is not stable as the acquiring processor may change
|
|
||||||
* when this function is called.
|
|
||||||
*
|
|
||||||
* @param lock Lock of SPI bus to get the acquiring device.
|
|
||||||
* @return The argument corresponding to the acquiring device, see
|
|
||||||
* `spi_bus_lock_register_dev`.
|
|
||||||
*/
|
|
||||||
spi_bus_lock_dev_handle_t spi_bus_lock_get_acquiring_dev(spi_bus_lock_handle_t lock);
|
|
||||||
|
|
||||||
/************* BG (Background, for ISR or cache) service *********************/
|
|
||||||
/**
|
|
||||||
* Call by a device to request a BG operation.
|
|
||||||
*
|
|
||||||
* Depending on the bus lock state, the BG operations may be resumed by this
|
|
||||||
* call, or pending until BG operations allowed.
|
|
||||||
*
|
|
||||||
* Cleared by `spi_bus_lock_bg_clear_req` in the BG.
|
|
||||||
*
|
|
||||||
* @param dev_handle The device requesting BG operations.
|
|
||||||
* @return always ESP_OK
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_lock_bg_request(spi_bus_lock_dev_handle_t dev_handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the ISR has finished all the BG operations for the acquiring device.
|
|
||||||
* If any `spi_bus_lock_bg_request` for this device has been called after
|
|
||||||
* `spi_bus_lock_acquire_start`, this function must be called before any operation
|
|
||||||
* in the task.
|
|
||||||
*
|
|
||||||
* @note Can only be called when bus acquired by this device.
|
|
||||||
*
|
|
||||||
* @param dev_handle Handle to the device acquiring the bus.
|
|
||||||
* @param wait Time to wait until timeout or succeed, must be `portMAX_DELAY` for now.
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: on success
|
|
||||||
* - ESP_ERR_INVALID_STATE: The device is not the acquiring bus.
|
|
||||||
* - ESP_ERR_INVALID_ARG: Timeout is not portMAX_DELAY.
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_lock_wait_bg_done(spi_bus_lock_dev_handle_t dev_handle, TickType_t wait);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle interrupt and closure of last operation. Should be called at the beginning of the ISR,
|
|
||||||
* when the ISR is acting as the acquiring processor.
|
|
||||||
*
|
|
||||||
* @param lock The SPI bus lock
|
|
||||||
*
|
|
||||||
* @return false if the ISR has already touched the HW, should run closure of the
|
|
||||||
* last operation first; otherwise true if the ISR just start operating
|
|
||||||
* on the HW, closure should be skipped.
|
|
||||||
*/
|
|
||||||
bool spi_bus_lock_bg_entry(spi_bus_lock_handle_t lock);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the scheduling of other acquiring devices, and control of HW operation
|
|
||||||
* status.
|
|
||||||
*
|
|
||||||
* If no BG request is found, call with `wip=false`. This function will return false,
|
|
||||||
* indicating there is incoming BG requests for the current acquiring device (or
|
|
||||||
* for all devices if there is no acquiring device) and the ISR needs retry.
|
|
||||||
* Otherwise may schedule a new acquiring processor (unblock the task) if there
|
|
||||||
* is, and return true.
|
|
||||||
*
|
|
||||||
* Otherwise if a BG request is started in this ISR, call with `wip=true` and the
|
|
||||||
* function will enable the interrupt to make the ISR be called again when the
|
|
||||||
* request is done.
|
|
||||||
*
|
|
||||||
* This function is safe and should still be called when the ISR just lost its acquiring processor
|
|
||||||
* role, but hasn't quit.
|
|
||||||
*
|
|
||||||
* @note This function will not change acquiring device. The ISR call
|
|
||||||
* `spi_bus_lock_bg_update_acquiring` to check for new acquiring device,
|
|
||||||
* when acquiring devices need to be served before other devices.
|
|
||||||
*
|
|
||||||
* @param lock The SPI bus lock.
|
|
||||||
* @param wip Whether an operation is being executed when quitting the ISR.
|
|
||||||
* @param do_yield[out] Not touched when no yielding required, otherwise set
|
|
||||||
* to pdTRUE.
|
|
||||||
* @return false if retry is required, indicating that there is pending BG request.
|
|
||||||
* otherwise true and quit ISR is allowed.
|
|
||||||
*/
|
|
||||||
bool spi_bus_lock_bg_exit(spi_bus_lock_handle_t lock, bool wip, BaseType_t* do_yield);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether there is device asking for the acquiring device, and the desired
|
|
||||||
* device for the next operation is also recommended.
|
|
||||||
*
|
|
||||||
* @note Must be called when the ISR is acting as the acquiring processor, and
|
|
||||||
* there is no acquiring device.
|
|
||||||
*
|
|
||||||
* @param lock The SPI bus lock.
|
|
||||||
* @param out_dev_lock The recommended device for hte next operation. It's the new
|
|
||||||
* acquiring device when found, otherwise a device that has active BG request.
|
|
||||||
*
|
|
||||||
* @return true if the ISR need to quit (new acquiring device has no active BG
|
|
||||||
* request, or no active BG requests for all devices when there is no
|
|
||||||
* acquiring device), otherwise false.
|
|
||||||
*/
|
|
||||||
bool spi_bus_lock_bg_check_dev_acq(spi_bus_lock_handle_t lock, spi_bus_lock_dev_handle_t *out_dev_lock);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the device has BG requests. Must be called when the ISR is acting as
|
|
||||||
* the acquiring processor.
|
|
||||||
*
|
|
||||||
* @note This is not stable, may become true again when a task request for BG
|
|
||||||
* operation (by `spi_bus_lock_bg_request`).
|
|
||||||
*
|
|
||||||
* @param dev_lock The device to check.
|
|
||||||
* @return true if the device has BG requests, otherwise false.
|
|
||||||
*/
|
|
||||||
bool spi_bus_lock_bg_check_dev_req(spi_bus_lock_dev_handle_t dev_lock);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the pending BG operation request of a device after served. Must be
|
|
||||||
* called when the ISR is acting as the acquiring processor.
|
|
||||||
*
|
|
||||||
* @note When the return value is true, the ISR will lost the acquiring processor role. Then
|
|
||||||
* `spi_bus_lock_bg_exit` must be called and checked before calling all other functions that
|
|
||||||
* require to be called when the ISR is the acquiring processor again.
|
|
||||||
*
|
|
||||||
* @param dev_handle The device whose request is served.
|
|
||||||
* @return True if no pending requests for the acquiring device, or for all devices
|
|
||||||
* if there is no acquiring device. Otherwise false. When the return value is
|
|
||||||
* true, the ISR is no longer the acquiring processor.
|
|
||||||
*/
|
|
||||||
bool spi_bus_lock_bg_clear_req(spi_bus_lock_dev_handle_t dev_lock);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there is any active BG requests.
|
|
||||||
*
|
|
||||||
* @param lock The SPI bus lock.
|
|
||||||
* @return true if any device has active BG requst, otherwise false.
|
|
||||||
*/
|
|
||||||
bool spi_bus_lock_bg_req_exist(spi_bus_lock_handle_t lock);
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Variable and APIs for the OS to initialize the locks for the main chip
|
|
||||||
******************************************************************************/
|
|
||||||
/// The lock for the main bus
|
|
||||||
extern const spi_bus_lock_handle_t g_main_spi_bus_lock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the main SPI bus, called during chip startup.
|
|
||||||
*
|
|
||||||
* @return always ESP_OK
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_lock_init_main_bus(void);
|
|
||||||
|
|
||||||
/// The lock for the main flash device
|
|
||||||
extern const spi_bus_lock_dev_handle_t g_spi_lock_main_flash_dev;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the main flash device, called during chip startup.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: if success
|
|
||||||
* - ESP_ERR_NO_MEM: memory exhausted
|
|
||||||
*/
|
|
||||||
esp_err_t spi_bus_lock_init_main_dev(void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "driver/spi_master.h"
|
#include "driver/spi_master.h"
|
||||||
#include "esp_private/periph_ctrl.h"
|
#include "esp_private/periph_ctrl.h"
|
||||||
#include "esp_private/spi_common_internal.h"
|
#include "esp_private/spi_common_internal.h"
|
||||||
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
#include "hal/spi_hal.h"
|
#include "hal/spi_hal.h"
|
||||||
#include "hal/gpio_hal.h"
|
#include "hal/gpio_hal.h"
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
@ -28,12 +29,6 @@
|
||||||
#include "hal/cache_ll.h"
|
#include "hal/cache_ll.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !SOC_RCC_IS_INDEPENDENT
|
|
||||||
#define SPI_COMMON_RCC_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC()
|
|
||||||
#else
|
|
||||||
#define SPI_COMMON_RCC_CLOCK_ATOMIC()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char *SPI_TAG = "spi";
|
static const char *SPI_TAG = "spi";
|
||||||
|
|
||||||
#define SPI_CHECK(a, str, ret_val) ESP_RETURN_ON_FALSE(a, ret_val, SPI_TAG, str)
|
#define SPI_CHECK(a, str, ret_val) ESP_RETURN_ON_FALSE(a, ret_val, SPI_TAG, str)
|
||||||
|
@ -67,20 +62,19 @@ typedef struct {
|
||||||
#endif
|
#endif
|
||||||
} spicommon_bus_context_t;
|
} spicommon_bus_context_t;
|
||||||
|
|
||||||
//Periph 1 is 'claimed' by SPI flash code.
|
|
||||||
static atomic_bool spi_periph_claimed[SOC_SPI_PERIPH_NUM] = { ATOMIC_VAR_INIT(true), ATOMIC_VAR_INIT(false),
|
|
||||||
#if (SOC_SPI_PERIPH_NUM >= 3)
|
|
||||||
ATOMIC_VAR_INIT(false),
|
|
||||||
#endif
|
|
||||||
#if (SOC_SPI_PERIPH_NUM >= 4)
|
|
||||||
ATOMIC_VAR_INIT(false),
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* spi_claiming_func[3] = {NULL, NULL, NULL};
|
|
||||||
static spicommon_bus_context_t s_mainbus = SPI_MAIN_BUS_DEFAULT();
|
static spicommon_bus_context_t s_mainbus = SPI_MAIN_BUS_DEFAULT();
|
||||||
static spicommon_bus_context_t* bus_ctx[SOC_SPI_PERIPH_NUM] = {&s_mainbus};
|
static spicommon_bus_context_t* bus_ctx[SOC_SPI_PERIPH_NUM] = {&s_mainbus};
|
||||||
|
|
||||||
|
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||||
|
/* The lock for the share SPI1 bus is registered here in a constructor due to need to access the context
|
||||||
|
This way we are able to decouple the SPI-flash driver from the spi-master driver */
|
||||||
|
static __attribute__((constructor)) void spi_bus_lock_init_main_bus(void)
|
||||||
|
{
|
||||||
|
/* Initialize bus context about the main SPI bus lock, called during chip startup. */
|
||||||
|
spi_bus_main_set_lock(g_main_spi_bus_lock);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !SOC_GDMA_SUPPORTED
|
#if !SOC_GDMA_SUPPORTED
|
||||||
//Each bit stands for 1 dma channel, BIT(0) should be used for SPI1
|
//Each bit stands for 1 dma channel, BIT(0) should be used for SPI1
|
||||||
static uint8_t spi_dma_chan_enabled = 0;
|
static uint8_t spi_dma_chan_enabled = 0;
|
||||||
|
@ -96,42 +90,6 @@ static inline bool is_valid_host(spi_host_device_t host)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------alloc spi periph-------------------------------------------------------//
|
|
||||||
//Returns true if this peripheral is successfully claimed, false if otherwise.
|
|
||||||
bool spicommon_periph_claim(spi_host_device_t host, const char* source)
|
|
||||||
{
|
|
||||||
bool false_var = false;
|
|
||||||
bool ret = atomic_compare_exchange_strong(&spi_periph_claimed[host], &false_var, true);
|
|
||||||
if (ret) {
|
|
||||||
spi_claiming_func[host] = source;
|
|
||||||
SPI_COMMON_RCC_CLOCK_ATOMIC() {
|
|
||||||
spi_ll_enable_bus_clock(host, true);
|
|
||||||
spi_ll_reset_register(host);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_EARLY_LOGE(SPI_TAG, "SPI%d already claimed by %s.", host + 1, spi_claiming_func[host]);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool spicommon_periph_in_use(spi_host_device_t host)
|
|
||||||
{
|
|
||||||
return atomic_load(&spi_periph_claimed[host]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Returns true if this peripheral is successfully freed, false if otherwise.
|
|
||||||
bool spicommon_periph_free(spi_host_device_t host)
|
|
||||||
{
|
|
||||||
bool true_var = true;
|
|
||||||
bool ret = atomic_compare_exchange_strong(&spi_periph_claimed[host], &true_var, false);
|
|
||||||
if (ret) {
|
|
||||||
SPI_COMMON_RCC_CLOCK_ATOMIC() {
|
|
||||||
spi_ll_enable_bus_clock(host, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int spicommon_irqsource_for_host(spi_host_device_t host)
|
int spicommon_irqsource_for_host(spi_host_device_t host)
|
||||||
{
|
{
|
||||||
return spi_periph_signal[host].irq;
|
return spi_periph_signal[host].irq;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "esp_private/spi_slave_internal.h"
|
#include "esp_private/spi_slave_internal.h"
|
||||||
#include "esp_private/spi_common_internal.h"
|
#include "esp_private/spi_common_internal.h"
|
||||||
#include "esp_private/esp_cache_private.h"
|
#include "esp_private/esp_cache_private.h"
|
||||||
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
|
|
||||||
static const char *SPI_TAG = "spi_slave";
|
static const char *SPI_TAG = "spi_slave";
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "freertos/ringbuf.h"
|
#include "freertos/ringbuf.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_private/spi_common_internal.h"
|
#include "esp_private/spi_common_internal.h"
|
||||||
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
#include "esp_private/esp_cache_private.h"
|
#include "esp_private/esp_cache_private.h"
|
||||||
#include "driver/spi_slave_hd.h"
|
#include "driver/spi_slave_hd.h"
|
||||||
#include "hal/spi_slave_hd_hal.h"
|
#include "hal/spi_slave_hd_hal.h"
|
||||||
|
|
|
@ -36,7 +36,9 @@ if(NOT BOOTLOADER_BUILD)
|
||||||
"port/${target}/io_mux.c"
|
"port/${target}/io_mux.c"
|
||||||
"port/${target}/esp_clk_tree.c"
|
"port/${target}/esp_clk_tree.c"
|
||||||
"port/esp_clk_tree_common.c"
|
"port/esp_clk_tree_common.c"
|
||||||
"dma/esp_dma_utils.c")
|
"dma/esp_dma_utils.c"
|
||||||
|
"spi_share_hw_ctrl.c"
|
||||||
|
"spi_bus_lock.c")
|
||||||
|
|
||||||
if(CONFIG_SOC_ADC_SUPPORTED)
|
if(CONFIG_SOC_ADC_SUPPORTED)
|
||||||
list(APPEND srcs "adc_share_hw_ctrl.c")
|
list(APPEND srcs "adc_share_hw_ctrl.c")
|
||||||
|
|
|
@ -341,4 +341,11 @@ menu "Hardware Settings"
|
||||||
clock support isn't done yet. So with this option,
|
clock support isn't done yet. So with this option,
|
||||||
we use xtal on FPGA as the clock source.
|
we use xtal on FPGA as the clock source.
|
||||||
|
|
||||||
|
config ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM
|
||||||
|
bool
|
||||||
|
default n
|
||||||
|
|
||||||
|
config ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM
|
||||||
|
bool
|
||||||
|
default n
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -0,0 +1,511 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "hal/spi_types.h"
|
||||||
|
#include "esp_private/periph_ctrl.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !SOC_RCC_IS_INDEPENDENT
|
||||||
|
#define SPI_COMMON_RCC_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||||
|
#else
|
||||||
|
#define SPI_COMMON_RCC_CLOCK_ATOMIC()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BUS_LOCK_DEBUG 0
|
||||||
|
|
||||||
|
#if BUS_LOCK_DEBUG
|
||||||
|
#define BUS_LOCK_DEBUG_EXECUTE_CHECK(x) assert(x)
|
||||||
|
#else
|
||||||
|
#define BUS_LOCK_DEBUG_EXECUTE_CHECK(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct spi_bus_lock_t;
|
||||||
|
struct spi_bus_lock_dev_t;
|
||||||
|
/// Handle to the lock of an SPI bus
|
||||||
|
typedef struct spi_bus_lock_t* spi_bus_lock_handle_t;
|
||||||
|
/// Handle to lock of one of the device on an SPI bus
|
||||||
|
typedef struct spi_bus_lock_dev_t* spi_bus_lock_dev_handle_t;
|
||||||
|
/// Background operation control function
|
||||||
|
typedef void (*bg_ctrl_func_t)(void*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to claim a SPI peripheral
|
||||||
|
*
|
||||||
|
* Call this if your driver wants to manage a SPI peripheral.
|
||||||
|
*
|
||||||
|
* @param host Peripheral to claim
|
||||||
|
* @param source The caller indentification string.
|
||||||
|
*
|
||||||
|
* @return True if peripheral is claimed successfully; false if peripheral already is claimed.
|
||||||
|
*/
|
||||||
|
bool spicommon_periph_claim(spi_host_device_t host, const char* source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the spi periph is in use.
|
||||||
|
*
|
||||||
|
* @param host Peripheral to check.
|
||||||
|
*
|
||||||
|
* @return True if in use, otherwise false.
|
||||||
|
*/
|
||||||
|
bool spicommon_periph_in_use(spi_host_device_t host);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the SPI peripheral so another driver can claim it.
|
||||||
|
*
|
||||||
|
* @param host Peripheral to return
|
||||||
|
*
|
||||||
|
* @return True if peripheral is returned successfully; false if peripheral was free to claim already.
|
||||||
|
*/
|
||||||
|
bool spicommon_periph_free(spi_host_device_t host);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* SPI Bus Lock for arbitration among SPI master (intr, polling) trans, SPI flash operations and
|
||||||
|
* flash/psram cache access.
|
||||||
|
*
|
||||||
|
* NON-PUBLIC API. Don't use it directly in applications.
|
||||||
|
*
|
||||||
|
* There is the main lock corresponding to an SPI bus, of which several devices (holding child
|
||||||
|
* locks) attaching to it. Each of the device is STRONGLY RECOMMENDED to be used in only one task
|
||||||
|
* to avoid concurrency issues.
|
||||||
|
*
|
||||||
|
* Terms:
|
||||||
|
* - BG operations (BackGround operations) means some transaction that will not immediately /
|
||||||
|
* explicitly be sent in the task. It can be some cache access, or interrupt transactions.
|
||||||
|
*
|
||||||
|
* - Operation: usage of the bus, for example, do SPI transactions.
|
||||||
|
*
|
||||||
|
* - Acquiring processor: the task or the ISR that is allowed to use the bus. No operations will be
|
||||||
|
* performed if there is no acquiring processor. A processor becomes the acquiring processor if
|
||||||
|
* it ask for that when no acquiring processor exist, otherwise it has to wait for the acquiring
|
||||||
|
* processor to handle over the role to it. The acquiring processor will and will only assign one
|
||||||
|
* acquiring processor in the waiting list (if not empty) when it finishes its operation.
|
||||||
|
*
|
||||||
|
* - Acquiring device: the only device allowed to use the bus. Operations can be performed in
|
||||||
|
* either the BG or the task. When there's no acquiring device, only the ISR is allowed to be the
|
||||||
|
* acquiring processor and perform operations on the bus.
|
||||||
|
*
|
||||||
|
* When a device wants to perform operations, it either:
|
||||||
|
* 1. Acquire the bus, and operate in the task (e.g. polling transactions of SPI master, and SPI flash
|
||||||
|
* operations)
|
||||||
|
*
|
||||||
|
* 2. Request a BG operation. And the ISR will be enabled at proper time.
|
||||||
|
*
|
||||||
|
* For example if a task wants to send an interrupt transaction, it prepares the data in the task,
|
||||||
|
* call `spi_bus_lock_bg_request`, and handle sending in the ISR.
|
||||||
|
*
|
||||||
|
* 3. When a device has already acquired the bus, BG operations are also allowed. After the
|
||||||
|
* `spi_bus_lock_bg_request` is called, call `spi_bus_lock_wait_bg_done` before operations in task
|
||||||
|
* again to wait until BG operations are done.
|
||||||
|
*
|
||||||
|
* Any device may try to invoke the ISR (by `spi_bus_lock_bg_request`). The ISR will be invoked and
|
||||||
|
* become the acquiring processor immediately when the bus is not acquired by other processors. Any
|
||||||
|
* device may also try to acquire the bus (by `spi_bus_lock_acquire_start`). The device will become
|
||||||
|
* the acquiring processor immediately when the bus is not acquired and there is no request active.
|
||||||
|
*
|
||||||
|
* The acquiring processor must be aware of its acquiring role, and properly transfer the acquiring
|
||||||
|
* processor to other tasks or ISR when they have nothing else to do. Before picking a new
|
||||||
|
* acquiring processor, a new acquiring device must be picked first, if there are other devices,
|
||||||
|
* asking to be acquiring device. After that, the new acquiring processor is picked by the sequence
|
||||||
|
* below:
|
||||||
|
*
|
||||||
|
* 1. If there is an acquiring device:
|
||||||
|
* 1.1 The ISR, if acquiring device has active BG requests
|
||||||
|
* 1.2 The task of the device, if no active BG request for the device
|
||||||
|
* 2. The ISR, if there's no acquiring device, but any BG request is active
|
||||||
|
* 3. No one becomes the acquiring processor
|
||||||
|
*
|
||||||
|
* The API also helps on the arbitration of SPI cs lines. The bus is initialized with a cs_num
|
||||||
|
* argument. When attaching devices onto the bus with `spi_bus_lock_register_dev`, it will allocate
|
||||||
|
* devices with different device ID according to the flags given. If the ID is smaller than the
|
||||||
|
* cs_num given when bus is initialized, error will be returned.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* * Initialization:
|
||||||
|
* 1. Call `spi_bus_init_lock` to register a lock for a bus.
|
||||||
|
* 2. Call `spi_bus_lock_set_bg_control` to prepare BG enable/disable functions for
|
||||||
|
* the lock.
|
||||||
|
* 3. Call `spi_bus_lock_register_dev` for each devices that may make use of the
|
||||||
|
* bus, properly store the returned handle, representing those devices.
|
||||||
|
*
|
||||||
|
* * Acquiring:
|
||||||
|
* 1. Call `spi_bus_lock_acquire_start` when a device wants to use the bus
|
||||||
|
* 2. Call `spi_bus_lock_touch` to mark the bus as touched by this device. Also check if the bus
|
||||||
|
* has been touched by other devices.
|
||||||
|
* 3. (optional) Do something on the bus...
|
||||||
|
* 4. (optional) Call `spi_bus_lock_bg_request` to inform and invoke the BG. See ISR below about
|
||||||
|
* ISR operations.
|
||||||
|
* 5. (optional) If `spi_bus_lock_bg_request` is done, you have to call `spi_bus_lock_wait_bg_done`
|
||||||
|
* before touching the bus again, or do the following steps.
|
||||||
|
* 6. Call `spi_bus_lock_acquire_end` to release the bus to other devices.
|
||||||
|
*
|
||||||
|
* * ISR:
|
||||||
|
* 1. Call `spi_bus_lock_bg_entry` when entering the ISR, run or skip the closure for the previous
|
||||||
|
* operation according to the return value.
|
||||||
|
* 2. Call `spi_bus_lock_get_acquiring_dev` to get the acquiring device. If there is no acquiring
|
||||||
|
* device, call `spi_bus_lock_bg_check_dev_acq` to check and update a new acquiring device.
|
||||||
|
* 3. Call `spi_bus_lock_bg_check_dev_req` to check for request of the desired device. If the
|
||||||
|
* desired device is not requested, go to step 5.
|
||||||
|
* 4. Check, start operation for the desired device and go to step 6; otherwise if no operations
|
||||||
|
* can be performed, call `spi_bus_lock_bg_clear_req` to clear the request for this device. If
|
||||||
|
* `spi_bus_lock_bg_clear_req` is called and there is no BG requests active, goto step 6.
|
||||||
|
* 5. (optional) If the device is the acquiring device, go to step 6, otherwise
|
||||||
|
* find another desired device, and go back to step 3.
|
||||||
|
* 6. Call `spi_bus_lock_bg_exit` to try quitting the ISR. If failed, go back to step 2 to look for
|
||||||
|
* a new request again. Otherwise, quit the ISR.
|
||||||
|
*
|
||||||
|
* * Deinitialization (optional):
|
||||||
|
* 1. Call `spi_bus_lock_unregister_dev` for each device when they are no longer needed.
|
||||||
|
* 2. Call `spi_bus_deinit_lock` to release the resources occupied by the lock.
|
||||||
|
*
|
||||||
|
* Some technical details:
|
||||||
|
*
|
||||||
|
* The child-lock of each device will have its own Binary Semaphore, which allows the task serving
|
||||||
|
* this device (task A) being blocked when it fail to become the acquiring processor while it's
|
||||||
|
* calling `spi_bus_lock_acquire_start` or `spi_bus_lock_wait_bg_done`. If it is blocked, there
|
||||||
|
* must be an acquiring processor (either the ISR or another task (task B)), is doing transaction
|
||||||
|
* on the bus. After that, task A will get unblocked and become the acquiring processor when the
|
||||||
|
* ISR call `spi_bus_lock_bg_resume_acquired_dev`, or task B call `spi_bus_lock_acquire_end`.
|
||||||
|
*
|
||||||
|
* When the device wants to send ISR transaction, it should call `spi_bus_lock_bg_request` after
|
||||||
|
* the data is prepared. This function sets a request bit in the critical resource. The ISR will be
|
||||||
|
* invoked and become the new acquiring processor, when:
|
||||||
|
*
|
||||||
|
* 1. A task calls `spi_bus_lock_bg_request` while there is no acquiring processor;
|
||||||
|
* 2. A tasks calls `spi_bus_lock_bg_request` while the task is the acquiring processor. Then the
|
||||||
|
* acquiring processor is handled over to the ISR;
|
||||||
|
* 3. A tasks who is the acquiring processor release the bus by calling `spi_bus_lock_acquire_end`,
|
||||||
|
* and the ISR happens to be the next acquiring processor.
|
||||||
|
*
|
||||||
|
* The ISR will check (by `spi_bus_lock_bg_check_dev_req`) and clear a request bit (by
|
||||||
|
* `spi_bus_lock_bg_clear_req`) after it confirm that all the requests of the corresponding device
|
||||||
|
* are served. The request bit supports being written to recursively, which means, the task don't
|
||||||
|
* need to wait for `spi_bus_lock_bg_clear_req` before call another `spi_bus_lock_bg_request`. The
|
||||||
|
* API will handle the concurrency conflicts properly.
|
||||||
|
*
|
||||||
|
* The `spi_bus_lock_bg_exit` (together with `spi_bus_lock_bg_entry` called before)` is responsible
|
||||||
|
* to ensure ONE and ONLY ONE of the following will happen when the ISR try to give up its
|
||||||
|
* acquiring processor rule:
|
||||||
|
*
|
||||||
|
* 1. ISR quit, no any task unblocked while the interrupt disabled, and none of the BG bits is
|
||||||
|
* active.
|
||||||
|
* 2. ISR quit, there is an acquiring device, and the acquiring processor is passed to the task
|
||||||
|
* serving the acquiring device by unblocking the task.
|
||||||
|
* 3. The ISR failed to quit and have to try again.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#define DEV_NUM_MAX 6 ///< Number of devices supported by this lock
|
||||||
|
|
||||||
|
/// Lock configuration struct
|
||||||
|
typedef struct {
|
||||||
|
int host_id; ///< SPI host id
|
||||||
|
int cs_num; ///< Physical cs numbers of the host
|
||||||
|
} spi_bus_lock_config_t;
|
||||||
|
|
||||||
|
/// Child-lock configuration struct
|
||||||
|
typedef struct {
|
||||||
|
uint32_t flags; ///< flags for the lock, OR-ed of `SPI_BUS_LOCK_DEV_*` flags.
|
||||||
|
#define SPI_BUS_LOCK_DEV_FLAG_CS_REQUIRED BIT(0) ///< The device needs a physical CS pin.
|
||||||
|
} spi_bus_lock_dev_config_t;
|
||||||
|
|
||||||
|
/************* Common *********************/
|
||||||
|
/**
|
||||||
|
* Initialize a lock for an SPI bus.
|
||||||
|
*
|
||||||
|
* @param out_lock Output of the handle to the lock
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_NO_MEM: if memory exhausted
|
||||||
|
* - ESP_OK: if success
|
||||||
|
*/
|
||||||
|
esp_err_t spi_bus_init_lock(spi_bus_lock_handle_t *out_lock, const spi_bus_lock_config_t *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the resources used by an SPI bus lock.
|
||||||
|
*
|
||||||
|
* @note All attached devices should have been unregistered before calling this
|
||||||
|
* funciton.
|
||||||
|
*
|
||||||
|
* @param lock Handle to the lock to free.
|
||||||
|
*/
|
||||||
|
void spi_bus_deinit_lock(spi_bus_lock_handle_t lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the corresponding lock according to bus id.
|
||||||
|
*
|
||||||
|
* @param host_id The bus id to get the lock
|
||||||
|
* @return The lock handle
|
||||||
|
*/
|
||||||
|
spi_bus_lock_handle_t spi_bus_lock_get_by_id(spi_host_device_t host_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure how the SPI bus lock enable the background operation.
|
||||||
|
*
|
||||||
|
* @note The lock will not try to stop the background operations, but wait for
|
||||||
|
* The background operations finished indicated by `spi_bus_lock_bg_resume_acquired_dev`.
|
||||||
|
*
|
||||||
|
* @param lock Handle to the lock to set
|
||||||
|
* @param bg_enable The enabling function
|
||||||
|
* @param bg_disable The disabling function, set to NULL if not required
|
||||||
|
* @param arg Argument to pass to the enabling/disabling function.
|
||||||
|
*/
|
||||||
|
void spi_bus_lock_set_bg_control(spi_bus_lock_handle_t lock, bg_ctrl_func_t bg_enable,
|
||||||
|
bg_ctrl_func_t bg_disable, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach a device onto an SPI bus lock. The returning handle is used to perform
|
||||||
|
* following requests for the attached device.
|
||||||
|
*
|
||||||
|
* @param lock SPI bus lock to attach
|
||||||
|
* @param out_dev_handle Output handle corresponding to the device
|
||||||
|
* @param flags requirement of the device, bitwise OR of SPI_BUS_LOCK_FLAG_* flags
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED: if there's no hardware resources for new devices.
|
||||||
|
* - ESP_ERR_NO_MEM: if memory exhausted
|
||||||
|
* - ESP_OK: if success
|
||||||
|
*/
|
||||||
|
esp_err_t spi_bus_lock_register_dev(spi_bus_lock_handle_t lock,
|
||||||
|
spi_bus_lock_dev_config_t *config,
|
||||||
|
spi_bus_lock_dev_handle_t *out_dev_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detach a device from its bus and free the resources used
|
||||||
|
*
|
||||||
|
* @param dev_handle Handle to the device.
|
||||||
|
*/
|
||||||
|
void spi_bus_lock_unregister_dev(spi_bus_lock_dev_handle_t dev_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the parent bus lock of the device
|
||||||
|
*
|
||||||
|
* @param dev_handle Handle to the device to get bus lock
|
||||||
|
* @return The bus lock handle
|
||||||
|
*/
|
||||||
|
spi_bus_lock_handle_t spi_bus_lock_get_parent(spi_bus_lock_dev_handle_t dev_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the device ID of a lock.
|
||||||
|
*
|
||||||
|
* The callers should allocate CS pins according to this ID.
|
||||||
|
*
|
||||||
|
* @param dev_handle Handle to the device to get ID
|
||||||
|
* @return ID of the device
|
||||||
|
*/
|
||||||
|
int spi_bus_lock_get_dev_id(spi_bus_lock_dev_handle_t dev_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The device request to touch bus registers. Can only be called by the acquiring processor.
|
||||||
|
*
|
||||||
|
* Also check if the registers has been touched by other devices.
|
||||||
|
*
|
||||||
|
* @param dev_handle Handle to the device to operate the registers
|
||||||
|
* @return true if there has been other devices touching SPI registers.
|
||||||
|
* The caller may need to do a full-configuration. Otherwise return
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
bool spi_bus_lock_touch(spi_bus_lock_dev_handle_t dev_handle);
|
||||||
|
|
||||||
|
/************* Acquiring service *********************/
|
||||||
|
/**
|
||||||
|
* Acquiring the SPI bus for exclusive use. Will also wait for the BG to finish all requests of
|
||||||
|
* this device before it returns.
|
||||||
|
*
|
||||||
|
* After successfully return, the caller becomes the acquiring processor.
|
||||||
|
*
|
||||||
|
* @note For the main flash bus, `bg_disable` will be called to disable the cache.
|
||||||
|
*
|
||||||
|
* @param dev_handle Handle to the device request for acquiring.
|
||||||
|
* @param wait Time to wait until timeout or succeed, must be `portMAX_DELAY` for now.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: on success
|
||||||
|
* - ESP_ERR_INVALID_ARG: timeout is not portMAX_DELAY
|
||||||
|
*/
|
||||||
|
esp_err_t spi_bus_lock_acquire_start(spi_bus_lock_dev_handle_t dev_handle, TickType_t wait);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the bus acquired. Will pass the acquiring processor to other blocked
|
||||||
|
* processors (tasks or ISR), and cause them to be unblocked or invoked.
|
||||||
|
*
|
||||||
|
* The acquiring device may also become NULL if no device is asking for acquiring.
|
||||||
|
* In this case, the BG may be invoked if there is any BG requests.
|
||||||
|
*
|
||||||
|
* If the new acquiring device has BG requests, the BG will be invoked before the
|
||||||
|
* task is resumed later after the BG finishes all requests of the new acquiring
|
||||||
|
* device. Otherwise the task of the new acquiring device will be resumed immediately.
|
||||||
|
*
|
||||||
|
* @param dev_handle Handle to the device releasing the bus.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: on success
|
||||||
|
* - ESP_ERR_INVALID_STATE: the device hasn't acquired the lock yet
|
||||||
|
*/
|
||||||
|
esp_err_t spi_bus_lock_acquire_end(spi_bus_lock_dev_handle_t dev_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the device acquiring the bus.
|
||||||
|
*
|
||||||
|
* @note Return value is not stable as the acquiring processor may change
|
||||||
|
* when this function is called.
|
||||||
|
*
|
||||||
|
* @param lock Lock of SPI bus to get the acquiring device.
|
||||||
|
* @return The argument corresponding to the acquiring device, see
|
||||||
|
* `spi_bus_lock_register_dev`.
|
||||||
|
*/
|
||||||
|
spi_bus_lock_dev_handle_t spi_bus_lock_get_acquiring_dev(spi_bus_lock_handle_t lock);
|
||||||
|
|
||||||
|
/************* BG (Background, for ISR or cache) service *********************/
|
||||||
|
/**
|
||||||
|
* Call by a device to request a BG operation.
|
||||||
|
*
|
||||||
|
* Depending on the bus lock state, the BG operations may be resumed by this
|
||||||
|
* call, or pending until BG operations allowed.
|
||||||
|
*
|
||||||
|
* Cleared by `spi_bus_lock_bg_clear_req` in the BG.
|
||||||
|
*
|
||||||
|
* @param dev_handle The device requesting BG operations.
|
||||||
|
* @return always ESP_OK
|
||||||
|
*/
|
||||||
|
esp_err_t spi_bus_lock_bg_request(spi_bus_lock_dev_handle_t dev_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the ISR has finished all the BG operations for the acquiring device.
|
||||||
|
* If any `spi_bus_lock_bg_request` for this device has been called after
|
||||||
|
* `spi_bus_lock_acquire_start`, this function must be called before any operation
|
||||||
|
* in the task.
|
||||||
|
*
|
||||||
|
* @note Can only be called when bus acquired by this device.
|
||||||
|
*
|
||||||
|
* @param dev_handle Handle to the device acquiring the bus.
|
||||||
|
* @param wait Time to wait until timeout or succeed, must be `portMAX_DELAY` for now.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: on success
|
||||||
|
* - ESP_ERR_INVALID_STATE: The device is not the acquiring bus.
|
||||||
|
* - ESP_ERR_INVALID_ARG: Timeout is not portMAX_DELAY.
|
||||||
|
*/
|
||||||
|
esp_err_t spi_bus_lock_wait_bg_done(spi_bus_lock_dev_handle_t dev_handle, TickType_t wait);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle interrupt and closure of last operation. Should be called at the beginning of the ISR,
|
||||||
|
* when the ISR is acting as the acquiring processor.
|
||||||
|
*
|
||||||
|
* @param lock The SPI bus lock
|
||||||
|
*
|
||||||
|
* @return false if the ISR has already touched the HW, should run closure of the
|
||||||
|
* last operation first; otherwise true if the ISR just start operating
|
||||||
|
* on the HW, closure should be skipped.
|
||||||
|
*/
|
||||||
|
bool spi_bus_lock_bg_entry(spi_bus_lock_handle_t lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the scheduling of other acquiring devices, and control of HW operation
|
||||||
|
* status.
|
||||||
|
*
|
||||||
|
* If no BG request is found, call with `wip=false`. This function will return false,
|
||||||
|
* indicating there is incoming BG requests for the current acquiring device (or
|
||||||
|
* for all devices if there is no acquiring device) and the ISR needs retry.
|
||||||
|
* Otherwise may schedule a new acquiring processor (unblock the task) if there
|
||||||
|
* is, and return true.
|
||||||
|
*
|
||||||
|
* Otherwise if a BG request is started in this ISR, call with `wip=true` and the
|
||||||
|
* function will enable the interrupt to make the ISR be called again when the
|
||||||
|
* request is done.
|
||||||
|
*
|
||||||
|
* This function is safe and should still be called when the ISR just lost its acquiring processor
|
||||||
|
* role, but hasn't quit.
|
||||||
|
*
|
||||||
|
* @note This function will not change acquiring device. The ISR call
|
||||||
|
* `spi_bus_lock_bg_update_acquiring` to check for new acquiring device,
|
||||||
|
* when acquiring devices need to be served before other devices.
|
||||||
|
*
|
||||||
|
* @param lock The SPI bus lock.
|
||||||
|
* @param wip Whether an operation is being executed when quitting the ISR.
|
||||||
|
* @param do_yield[out] Not touched when no yielding required, otherwise set
|
||||||
|
* to pdTRUE.
|
||||||
|
* @return false if retry is required, indicating that there is pending BG request.
|
||||||
|
* otherwise true and quit ISR is allowed.
|
||||||
|
*/
|
||||||
|
bool spi_bus_lock_bg_exit(spi_bus_lock_handle_t lock, bool wip, BaseType_t* do_yield);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether there is device asking for the acquiring device, and the desired
|
||||||
|
* device for the next operation is also recommended.
|
||||||
|
*
|
||||||
|
* @note Must be called when the ISR is acting as the acquiring processor, and
|
||||||
|
* there is no acquiring device.
|
||||||
|
*
|
||||||
|
* @param lock The SPI bus lock.
|
||||||
|
* @param out_dev_lock The recommended device for hte next operation. It's the new
|
||||||
|
* acquiring device when found, otherwise a device that has active BG request.
|
||||||
|
*
|
||||||
|
* @return true if the ISR need to quit (new acquiring device has no active BG
|
||||||
|
* request, or no active BG requests for all devices when there is no
|
||||||
|
* acquiring device), otherwise false.
|
||||||
|
*/
|
||||||
|
bool spi_bus_lock_bg_check_dev_acq(spi_bus_lock_handle_t lock, spi_bus_lock_dev_handle_t *out_dev_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the device has BG requests. Must be called when the ISR is acting as
|
||||||
|
* the acquiring processor.
|
||||||
|
*
|
||||||
|
* @note This is not stable, may become true again when a task request for BG
|
||||||
|
* operation (by `spi_bus_lock_bg_request`).
|
||||||
|
*
|
||||||
|
* @param dev_lock The device to check.
|
||||||
|
* @return true if the device has BG requests, otherwise false.
|
||||||
|
*/
|
||||||
|
bool spi_bus_lock_bg_check_dev_req(spi_bus_lock_dev_handle_t dev_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the pending BG operation request of a device after served. Must be
|
||||||
|
* called when the ISR is acting as the acquiring processor.
|
||||||
|
*
|
||||||
|
* @note When the return value is true, the ISR will lost the acquiring processor role. Then
|
||||||
|
* `spi_bus_lock_bg_exit` must be called and checked before calling all other functions that
|
||||||
|
* require to be called when the ISR is the acquiring processor again.
|
||||||
|
*
|
||||||
|
* @param dev_handle The device whose request is served.
|
||||||
|
* @return True if no pending requests for the acquiring device, or for all devices
|
||||||
|
* if there is no acquiring device. Otherwise false. When the return value is
|
||||||
|
* true, the ISR is no longer the acquiring processor.
|
||||||
|
*/
|
||||||
|
bool spi_bus_lock_bg_clear_req(spi_bus_lock_dev_handle_t dev_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is any active BG requests.
|
||||||
|
*
|
||||||
|
* @param lock The SPI bus lock.
|
||||||
|
* @return true if any device has active BG requst, otherwise false.
|
||||||
|
*/
|
||||||
|
bool spi_bus_lock_bg_req_exist(spi_bus_lock_handle_t lock);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Variable and APIs for the OS to initialize the locks for the main chip
|
||||||
|
******************************************************************************/
|
||||||
|
/// The lock for the main bus
|
||||||
|
extern const spi_bus_lock_handle_t g_main_spi_bus_lock;
|
||||||
|
|
||||||
|
/// The lock for the main flash device
|
||||||
|
extern const spi_bus_lock_dev_handle_t g_spi_lock_main_flash_dev;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the main flash device, called during chip startup.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: if success
|
||||||
|
* - ESP_ERR_NO_MEM: memory exhausted
|
||||||
|
*/
|
||||||
|
esp_err_t spi_bus_lock_init_main_dev(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_private/spi_common_internal.h"
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "soc/soc_caps.h"
|
#include "soc/soc_caps.h"
|
||||||
#include "stdatomic.h"
|
#include "stdatomic.h"
|
||||||
|
@ -17,6 +17,18 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM
|
||||||
|
#define SPI_BUS_LOCK_ISR_ATTR IRAM_ATTR
|
||||||
|
#else
|
||||||
|
#define SPI_BUS_LOCK_ISR_ATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_SPI_BUS_LOCK_FUNCS_IN_IRAM
|
||||||
|
#define SPI_BUSLOCK_ATTR IRAM_ATTR
|
||||||
|
#else
|
||||||
|
#define SPI_BUSLOCK_ATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This lock is designed to solve the conflicts between SPI devices (used in tasks) and
|
* This lock is designed to solve the conflicts between SPI devices (used in tasks) and
|
||||||
* the background operations (ISR or cache access).
|
* the background operations (ISR or cache access).
|
||||||
|
@ -262,7 +274,7 @@ static inline int dev_lock_get_id(spi_bus_lock_dev_t *dev_lock);
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* atomic operations to the status
|
* atomic operations to the status
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
SPI_MASTER_ISR_ATTR static inline uint32_t lock_status_fetch_set(spi_bus_lock_t *lock, uint32_t set)
|
SPI_BUS_LOCK_ISR_ATTR static inline uint32_t lock_status_fetch_set(spi_bus_lock_t *lock, uint32_t set)
|
||||||
{
|
{
|
||||||
return atomic_fetch_or(&lock->status, set);
|
return atomic_fetch_or(&lock->status, set);
|
||||||
}
|
}
|
||||||
|
@ -277,7 +289,7 @@ IRAM_ATTR static inline uint32_t lock_status_fetch(spi_bus_lock_t *lock)
|
||||||
return atomic_load(&lock->status);
|
return atomic_load(&lock->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR static inline void lock_status_init(spi_bus_lock_t *lock)
|
SPI_BUS_LOCK_ISR_ATTR static inline void lock_status_init(spi_bus_lock_t *lock)
|
||||||
{
|
{
|
||||||
atomic_store(&lock->status, 0);
|
atomic_store(&lock->status, 0);
|
||||||
}
|
}
|
||||||
|
@ -300,7 +312,7 @@ IRAM_ATTR static inline uint32_t lock_status_clear(spi_bus_lock_t* lock, uint32_
|
||||||
* Most of them should be atomic, and special attention should be paid to the operation
|
* Most of them should be atomic, and special attention should be paid to the operation
|
||||||
* sequence.
|
* sequence.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
SPI_MASTER_ISR_ATTR static inline void resume_dev_in_isr(spi_bus_lock_dev_t *dev_lock, BaseType_t *do_yield)
|
SPI_BUS_LOCK_ISR_ATTR static inline void resume_dev_in_isr(spi_bus_lock_dev_t *dev_lock, BaseType_t *do_yield)
|
||||||
{
|
{
|
||||||
xSemaphoreGiveFromISR(dev_lock->semphr, do_yield);
|
xSemaphoreGiveFromISR(dev_lock->semphr, do_yield);
|
||||||
}
|
}
|
||||||
|
@ -310,7 +322,7 @@ IRAM_ATTR static inline void resume_dev(const spi_bus_lock_dev_t *dev_lock)
|
||||||
xSemaphoreGive(dev_lock->semphr);
|
xSemaphoreGive(dev_lock->semphr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR static inline void bg_disable(spi_bus_lock_t *lock)
|
SPI_BUS_LOCK_ISR_ATTR static inline void bg_disable(spi_bus_lock_t *lock)
|
||||||
{
|
{
|
||||||
BUS_LOCK_DEBUG_EXECUTE_CHECK(lock->bg_disable);
|
BUS_LOCK_DEBUG_EXECUTE_CHECK(lock->bg_disable);
|
||||||
lock->bg_disable(lock->bg_arg);
|
lock->bg_disable(lock->bg_arg);
|
||||||
|
@ -324,7 +336,7 @@ IRAM_ATTR static inline void bg_enable(spi_bus_lock_t* lock)
|
||||||
|
|
||||||
// Set the REQ bit. If we become the acquiring processor, invoke the ISR and pass that to it.
|
// Set the REQ bit. If we become the acquiring processor, invoke the ISR and pass that to it.
|
||||||
// The caller will never become the acquiring processor after this function returns.
|
// The caller will never become the acquiring processor after this function returns.
|
||||||
SPI_MASTER_ATTR static inline void req_core(spi_bus_lock_dev_t *dev_handle)
|
SPI_BUSLOCK_ATTR static inline void req_core(spi_bus_lock_dev_t *dev_handle)
|
||||||
{
|
{
|
||||||
spi_bus_lock_t *lock = dev_handle->parent;
|
spi_bus_lock_t *lock = dev_handle->parent;
|
||||||
|
|
||||||
|
@ -351,7 +363,7 @@ SPI_MASTER_ATTR static inline void req_core(spi_bus_lock_dev_t *dev_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set the LOCK bit. Handle related stuff and return true if we become the acquiring processor.
|
//Set the LOCK bit. Handle related stuff and return true if we become the acquiring processor.
|
||||||
SPI_MASTER_ISR_ATTR static inline bool acquire_core(spi_bus_lock_dev_t *dev_handle)
|
SPI_BUS_LOCK_ISR_ATTR static inline bool acquire_core(spi_bus_lock_dev_t *dev_handle)
|
||||||
{
|
{
|
||||||
spi_bus_lock_t* lock = dev_handle->parent;
|
spi_bus_lock_t* lock = dev_handle->parent;
|
||||||
|
|
||||||
|
@ -455,7 +467,7 @@ IRAM_ATTR static inline void acquire_end_core(spi_bus_lock_dev_t *dev_handle)
|
||||||
|
|
||||||
// Move the REQ bits to corresponding PEND bits. Must be called by acquiring processor.
|
// Move the REQ bits to corresponding PEND bits. Must be called by acquiring processor.
|
||||||
// Have no side effects on the acquiring device/processor.
|
// Have no side effects on the acquiring device/processor.
|
||||||
SPI_MASTER_ISR_ATTR static inline void update_pend_core(spi_bus_lock_t *lock, uint32_t status)
|
SPI_BUS_LOCK_ISR_ATTR static inline void update_pend_core(spi_bus_lock_t *lock, uint32_t status)
|
||||||
{
|
{
|
||||||
uint32_t active_req_bits = status & REQ_MASK;
|
uint32_t active_req_bits = status & REQ_MASK;
|
||||||
#if PENDING_SHIFT > REQ_SHIFT
|
#if PENDING_SHIFT > REQ_SHIFT
|
||||||
|
@ -472,7 +484,7 @@ SPI_MASTER_ISR_ATTR static inline void update_pend_core(spi_bus_lock_t *lock, ui
|
||||||
// Clear the PEND bit (not REQ bit!) of a device, return the suggestion whether we can try to quit the ISR.
|
// Clear the PEND bit (not REQ bit!) of a device, return the suggestion whether we can try to quit the ISR.
|
||||||
// Lost the acquiring processor immediately when the BG bits for active device are inactive, indiciating by the return value.
|
// Lost the acquiring processor immediately when the BG bits for active device are inactive, indiciating by the return value.
|
||||||
// Can be called only when ISR is acting as the acquiring processor.
|
// Can be called only when ISR is acting as the acquiring processor.
|
||||||
SPI_MASTER_ISR_ATTR static inline bool clear_pend_core(spi_bus_lock_dev_t *dev_handle)
|
SPI_BUS_LOCK_ISR_ATTR static inline bool clear_pend_core(spi_bus_lock_dev_t *dev_handle)
|
||||||
{
|
{
|
||||||
bool finished;
|
bool finished;
|
||||||
spi_bus_lock_t *lock = dev_handle->parent;
|
spi_bus_lock_t *lock = dev_handle->parent;
|
||||||
|
@ -495,7 +507,7 @@ SPI_MASTER_ISR_ATTR static inline bool clear_pend_core(spi_bus_lock_dev_t *dev_h
|
||||||
// Return true if the ISR has already touched the HW, which means previous operations should
|
// Return true if the ISR has already touched the HW, which means previous operations should
|
||||||
// be terminated first, before we use the HW again. Otherwise return false.
|
// be terminated first, before we use the HW again. Otherwise return false.
|
||||||
// In either case `in_isr` will be marked as true, until call to `bg_exit_core` with `wip=false` successfully.
|
// In either case `in_isr` will be marked as true, until call to `bg_exit_core` with `wip=false` successfully.
|
||||||
SPI_MASTER_ISR_ATTR static inline bool bg_entry_core(spi_bus_lock_t *lock)
|
SPI_BUS_LOCK_ISR_ATTR static inline bool bg_entry_core(spi_bus_lock_t *lock)
|
||||||
{
|
{
|
||||||
BUS_LOCK_DEBUG_EXECUTE_CHECK(!lock->acquiring_dev || lock->acq_dev_bg_active);
|
BUS_LOCK_DEBUG_EXECUTE_CHECK(!lock->acquiring_dev || lock->acq_dev_bg_active);
|
||||||
/*
|
/*
|
||||||
|
@ -521,7 +533,7 @@ SPI_MASTER_ISR_ATTR static inline bool bg_entry_core(spi_bus_lock_t *lock)
|
||||||
// When called with `wip=true`, means the ISR is performing some operations. Will enable the interrupt again and exit unconditionally.
|
// When called with `wip=true`, means the ISR is performing some operations. Will enable the interrupt again and exit unconditionally.
|
||||||
// When called with `wip=false`, will only return `true` when there is no coming BG request. If return value is `false`, the ISR should try again.
|
// When called with `wip=false`, will only return `true` when there is no coming BG request. If return value is `false`, the ISR should try again.
|
||||||
// Will not change acquiring device.
|
// Will not change acquiring device.
|
||||||
SPI_MASTER_ISR_ATTR static inline bool bg_exit_core(spi_bus_lock_t *lock, bool wip, BaseType_t *do_yield)
|
SPI_BUS_LOCK_ISR_ATTR static inline bool bg_exit_core(spi_bus_lock_t *lock, bool wip, BaseType_t *do_yield)
|
||||||
{
|
{
|
||||||
//See comments in `bg_entry_core`, re-enable interrupt disabled in entry if we do need the interrupt
|
//See comments in `bg_entry_core`, re-enable interrupt disabled in entry if we do need the interrupt
|
||||||
if (wip) {
|
if (wip) {
|
||||||
|
@ -558,7 +570,7 @@ IRAM_ATTR static inline void dev_wait_prepare(spi_bus_lock_dev_t *dev_handle)
|
||||||
xSemaphoreTake(dev_handle->semphr, 0);
|
xSemaphoreTake(dev_handle->semphr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR static inline esp_err_t dev_wait(spi_bus_lock_dev_t *dev_handle, TickType_t wait)
|
SPI_BUS_LOCK_ISR_ATTR static inline esp_err_t dev_wait(spi_bus_lock_dev_t *dev_handle, TickType_t wait)
|
||||||
{
|
{
|
||||||
BaseType_t ret = xSemaphoreTake(dev_handle->semphr, wait);
|
BaseType_t ret = xSemaphoreTake(dev_handle->semphr, wait);
|
||||||
|
|
||||||
|
@ -748,7 +760,7 @@ IRAM_ATTR esp_err_t spi_bus_lock_acquire_end(spi_bus_lock_dev_t *dev_handle)
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR spi_bus_lock_dev_handle_t spi_bus_lock_get_acquiring_dev(spi_bus_lock_t *lock)
|
SPI_BUS_LOCK_ISR_ATTR spi_bus_lock_dev_handle_t spi_bus_lock_get_acquiring_dev(spi_bus_lock_t *lock)
|
||||||
{
|
{
|
||||||
return lock->acquiring_dev;
|
return lock->acquiring_dev;
|
||||||
}
|
}
|
||||||
|
@ -756,17 +768,17 @@ SPI_MASTER_ISR_ATTR spi_bus_lock_dev_handle_t spi_bus_lock_get_acquiring_dev(spi
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* BG (background operation) service
|
* BG (background operation) service
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_entry(spi_bus_lock_t* lock)
|
SPI_BUS_LOCK_ISR_ATTR bool spi_bus_lock_bg_entry(spi_bus_lock_t* lock)
|
||||||
{
|
{
|
||||||
return bg_entry_core(lock);
|
return bg_entry_core(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_exit(spi_bus_lock_t* lock, bool wip, BaseType_t* do_yield)
|
SPI_BUS_LOCK_ISR_ATTR bool spi_bus_lock_bg_exit(spi_bus_lock_t* lock, bool wip, BaseType_t* do_yield)
|
||||||
{
|
{
|
||||||
return bg_exit_core(lock, wip, do_yield);
|
return bg_exit_core(lock, wip, do_yield);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ATTR esp_err_t spi_bus_lock_bg_request(spi_bus_lock_dev_t *dev_handle)
|
SPI_BUSLOCK_ATTR esp_err_t spi_bus_lock_bg_request(spi_bus_lock_dev_t *dev_handle)
|
||||||
{
|
{
|
||||||
req_core(dev_handle);
|
req_core(dev_handle);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
@ -799,14 +811,14 @@ IRAM_ATTR esp_err_t spi_bus_lock_wait_bg_done(spi_bus_lock_dev_handle_t dev_hand
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_clear_req(spi_bus_lock_dev_t *dev_handle)
|
SPI_BUS_LOCK_ISR_ATTR bool spi_bus_lock_bg_clear_req(spi_bus_lock_dev_t *dev_handle)
|
||||||
{
|
{
|
||||||
bool finished = clear_pend_core(dev_handle);
|
bool finished = clear_pend_core(dev_handle);
|
||||||
ESP_EARLY_LOGV(TAG, "dev %d served from bg.", dev_lock_get_id(dev_handle));
|
ESP_EARLY_LOGV(TAG, "dev %d served from bg.", dev_lock_get_id(dev_handle));
|
||||||
return finished;
|
return finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_check_dev_acq(spi_bus_lock_t *lock,
|
SPI_BUS_LOCK_ISR_ATTR bool spi_bus_lock_bg_check_dev_acq(spi_bus_lock_t *lock,
|
||||||
spi_bus_lock_dev_handle_t *out_dev_lock)
|
spi_bus_lock_dev_handle_t *out_dev_lock)
|
||||||
{
|
{
|
||||||
BUS_LOCK_DEBUG_EXECUTE_CHECK(!lock->acquiring_dev);
|
BUS_LOCK_DEBUG_EXECUTE_CHECK(!lock->acquiring_dev);
|
||||||
|
@ -814,7 +826,7 @@ SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_check_dev_acq(spi_bus_lock_t *lock,
|
||||||
return schedule_core(lock, status, out_dev_lock);
|
return schedule_core(lock, status, out_dev_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_check_dev_req(spi_bus_lock_dev_t *dev_lock)
|
SPI_BUS_LOCK_ISR_ATTR bool spi_bus_lock_bg_check_dev_req(spi_bus_lock_dev_t *dev_lock)
|
||||||
{
|
{
|
||||||
spi_bus_lock_t* lock = dev_lock->parent;
|
spi_bus_lock_t* lock = dev_lock->parent;
|
||||||
uint32_t status = lock_status_fetch(lock);
|
uint32_t status = lock_status_fetch(lock);
|
||||||
|
@ -830,7 +842,7 @@ SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_check_dev_req(spi_bus_lock_dev_t *dev_l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_MASTER_ISR_ATTR bool spi_bus_lock_bg_req_exist(spi_bus_lock_t *lock)
|
SPI_BUS_LOCK_ISR_ATTR bool spi_bus_lock_bg_req_exist(spi_bus_lock_t *lock)
|
||||||
{
|
{
|
||||||
uint32_t status = lock_status_fetch(lock);
|
uint32_t status = lock_status_fetch(lock);
|
||||||
return status & BG_MASK;
|
return status & BG_MASK;
|
||||||
|
@ -855,12 +867,6 @@ static spi_bus_lock_t main_spi_bus_lock = {
|
||||||
};
|
};
|
||||||
const spi_bus_lock_handle_t g_main_spi_bus_lock = &main_spi_bus_lock;
|
const spi_bus_lock_handle_t g_main_spi_bus_lock = &main_spi_bus_lock;
|
||||||
|
|
||||||
esp_err_t spi_bus_lock_init_main_bus(void)
|
|
||||||
{
|
|
||||||
spi_bus_main_set_lock(g_main_spi_bus_lock);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StaticSemaphore_t main_flash_semphr;
|
static StaticSemaphore_t main_flash_semphr;
|
||||||
|
|
||||||
static spi_bus_lock_dev_t lock_main_flash_dev = {
|
static spi_bus_lock_dev_t lock_main_flash_dev = {
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "hal/spi_ll.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
static const char* SPI_TAG = "spi_share_hw_ctrl";
|
||||||
|
|
||||||
|
//Periph 1 is 'claimed' by SPI flash code.
|
||||||
|
static atomic_bool spi_periph_claimed[SOC_SPI_PERIPH_NUM] = { ATOMIC_VAR_INIT(true), ATOMIC_VAR_INIT(false),
|
||||||
|
#if (SOC_SPI_PERIPH_NUM >= 3)
|
||||||
|
ATOMIC_VAR_INIT(false),
|
||||||
|
#endif
|
||||||
|
#if (SOC_SPI_PERIPH_NUM >= 4)
|
||||||
|
ATOMIC_VAR_INIT(false),
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* spi_claiming_func[3] = {NULL, NULL, NULL};
|
||||||
|
|
||||||
|
//----------------------------------------------------------alloc spi periph-------------------------------------------------------//
|
||||||
|
//Returns true if this peripheral is successfully claimed, false if otherwise.
|
||||||
|
bool spicommon_periph_claim(spi_host_device_t host, const char* source)
|
||||||
|
{
|
||||||
|
bool false_var = false;
|
||||||
|
bool ret = atomic_compare_exchange_strong(&spi_periph_claimed[host], &false_var, true);
|
||||||
|
if (ret) {
|
||||||
|
spi_claiming_func[host] = source;
|
||||||
|
SPI_COMMON_RCC_CLOCK_ATOMIC() {
|
||||||
|
spi_ll_enable_bus_clock(host, true);
|
||||||
|
spi_ll_reset_register(host);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_EARLY_LOGE(SPI_TAG, "SPI%d already claimed by %s.", host + 1, spi_claiming_func[host]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool spicommon_periph_in_use(spi_host_device_t host)
|
||||||
|
{
|
||||||
|
return atomic_load(&spi_periph_claimed[host]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns true if this peripheral is successfully freed, false if otherwise.
|
||||||
|
bool spicommon_periph_free(spi_host_device_t host)
|
||||||
|
{
|
||||||
|
bool true_var = true;
|
||||||
|
bool ret = atomic_compare_exchange_strong(&spi_periph_claimed[host], &true_var, false);
|
||||||
|
if (ret) {
|
||||||
|
SPI_COMMON_RCC_CLOCK_ATOMIC() {
|
||||||
|
spi_ll_enable_bus_clock(host, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -63,11 +63,9 @@ else()
|
||||||
# [refactor-todo] requirements due to init code,
|
# [refactor-todo] requirements due to init code,
|
||||||
# should be removable once using component init functions
|
# should be removable once using component init functions
|
||||||
# link-time registration is used.
|
# link-time registration is used.
|
||||||
# [refactor-todo] requires "esp_driver_spi" for headers:
|
|
||||||
# - spi_common_internal.h
|
|
||||||
# [refactor-todo] esp_partition required for virtual efuse
|
# [refactor-todo] esp_partition required for virtual efuse
|
||||||
# init code. Move to esp_efuse component.
|
# init code. Move to esp_efuse component.
|
||||||
pthread bootloader_support efuse esp_driver_spi esp_partition
|
pthread bootloader_support efuse esp_partition esp_pm
|
||||||
LDFRAGMENTS "linker.lf" "app.lf")
|
LDFRAGMENTS "linker.lf" "app.lf")
|
||||||
add_subdirectory(port)
|
add_subdirectory(port)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "bootloader_clock.h"
|
#include "bootloader_clock.h"
|
||||||
#include "hal/wdt_hal.h"
|
#include "hal/wdt_hal.h"
|
||||||
|
|
||||||
#include "esp_private/spi_common_internal.h" // [refactor-todo]: for spicommon_periph_in_use
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_cpu.h"
|
#include "esp_cpu.h"
|
||||||
|
|
|
@ -49,7 +49,7 @@ else()
|
||||||
|
|
||||||
list(APPEND srcs ${cache_srcs})
|
list(APPEND srcs ${cache_srcs})
|
||||||
set(priv_requires bootloader_support app_update soc esp_mm
|
set(priv_requires bootloader_support app_update soc esp_mm
|
||||||
esp_driver_gpio esp_driver_spi # TODO: IDF-8503 move spi_bus_lock to esp_hw_support component
|
esp_driver_gpio
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#include "hal/spi_types.h"
|
#include "hal/spi_types.h"
|
||||||
#include "esp_private/spi_common_internal.h"
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
#include "hal/spi_flash_hal.h"
|
#include "hal/spi_flash_hal.h"
|
||||||
#include "hal/gpio_hal.h"
|
#include "hal/gpio_hal.h"
|
||||||
#include "esp_flash_internal.h"
|
#include "esp_flash_internal.h"
|
||||||
|
@ -165,6 +165,17 @@ static bool use_bus_lock(int host_id)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool bus_using_iomux(spi_host_device_t host)
|
||||||
|
{
|
||||||
|
#define CHECK_IOMUX_PIN(HOST, PIN_NAME) if (GPIO.func_in_sel_cfg[spi_periph_signal[(HOST)].PIN_NAME##_in].sig_in_sel) return false
|
||||||
|
|
||||||
|
CHECK_IOMUX_PIN(host, spid);
|
||||||
|
CHECK_IOMUX_PIN(host, spiq);
|
||||||
|
CHECK_IOMUX_PIN(host, spiwp);
|
||||||
|
CHECK_IOMUX_PIN(host, spihd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t acquire_spi_device(const esp_flash_spi_device_config_t *config, int* out_dev_id, spi_bus_lock_dev_handle_t* out_dev_handle)
|
static esp_err_t acquire_spi_device(const esp_flash_spi_device_config_t *config, int* out_dev_id, spi_bus_lock_dev_handle_t* out_dev_handle)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
|
@ -246,7 +257,7 @@ esp_err_t spi_bus_add_flash_device(esp_flash_t **out_chip, const esp_flash_spi_d
|
||||||
|
|
||||||
//avoid conflicts with main flash
|
//avoid conflicts with main flash
|
||||||
assert(config->host_id != SPI1_HOST || dev_id != 0);
|
assert(config->host_id != SPI1_HOST || dev_id != 0);
|
||||||
bool use_iomux = spicommon_bus_using_iomux(config->host_id);
|
bool use_iomux = bus_using_iomux(config->host_id);
|
||||||
memspi_host_config_t host_cfg = {
|
memspi_host_config_t host_cfg = {
|
||||||
.host_id = config->host_id,
|
.host_id = config->host_id,
|
||||||
.cs_num = dev_id,
|
.cs_num = dev_id,
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "esp_private/spi_common_internal.h"
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#include "esp_flash.h"
|
#include "esp_flash.h"
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "esp_private/spi_flash_os.h"
|
#include "esp_private/spi_flash_os.h"
|
||||||
#include "esp_private/cache_utils.h"
|
#include "esp_private/cache_utils.h"
|
||||||
|
|
||||||
#include "esp_private/spi_common_internal.h"
|
#include "esp_private/spi_share_hw_ctrl.h"
|
||||||
|
|
||||||
#define SPI_FLASH_CACHE_NO_DISABLE (CONFIG_SPI_FLASH_AUTO_SUSPEND || (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM)
|
#define SPI_FLASH_CACHE_NO_DISABLE (CONFIG_SPI_FLASH_AUTO_SUSPEND || (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM)
|
||||||
static const char TAG[] = "spi_flash";
|
static const char TAG[] = "spi_flash";
|
||||||
|
@ -326,7 +326,7 @@ esp_err_t esp_flash_init_main_bus_lock(void)
|
||||||
* is set. Thus, we must not call them if the macro is not defined, else the linker
|
* is set. Thus, we must not call them if the macro is not defined, else the linker
|
||||||
* would trigger errors. */
|
* would trigger errors. */
|
||||||
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||||
spi_bus_lock_init_main_bus();
|
/* bus_lock is registered by `spi_bus_lock_init_main_bus` constructor in spi_common.c */
|
||||||
spi_bus_lock_set_bg_control(g_main_spi_bus_lock, cache_enable, cache_disable, NULL);
|
spi_bus_lock_set_bg_control(g_main_spi_bus_lock, cache_enable, cache_disable, NULL);
|
||||||
|
|
||||||
esp_err_t err = spi_bus_lock_init_main_dev();
|
esp_err_t err = spi_bus_lock_init_main_dev();
|
||||||
|
|
|
@ -33,9 +33,9 @@ set(extra_components_which_shouldnt_be_included
|
||||||
bootloader_support
|
bootloader_support
|
||||||
# [refactor-todo]: should cxx be in G1? Can it exist without FreeRTOS?
|
# [refactor-todo]: should cxx be in G1? Can it exist without FreeRTOS?
|
||||||
cxx
|
cxx
|
||||||
# [refactor-todo]: driver is a dependency of esp_pm, spi_flash, vfs, esp_wifi
|
# [refactor-todo]: esp_driver_gpio is a dependency of esp_pm (should be removed from g1 builds),
|
||||||
# all of these should be removed from G1 except for spi_flash.
|
# spi_flash, esp_hw_support
|
||||||
esp_driver_gpio esp_driver_spi
|
esp_driver_gpio
|
||||||
# esp_app_format is dependency of bootloader_support, app_update
|
# esp_app_format is dependency of bootloader_support, app_update
|
||||||
esp_app_format
|
esp_app_format
|
||||||
# esp_bootloader_format is dependency of bootloader_support, app_update
|
# esp_bootloader_format is dependency of bootloader_support, app_update
|
||||||
|
@ -48,8 +48,6 @@ set(extra_components_which_shouldnt_be_included
|
||||||
# conditional on related Kconfig option. It is also used by esp_wifi, driver, mbedtls,
|
# conditional on related Kconfig option. It is also used by esp_wifi, driver, mbedtls,
|
||||||
# all of which should be removed from G1-only build.
|
# all of which should be removed from G1-only build.
|
||||||
esp_pm
|
esp_pm
|
||||||
# esp_ringbuf is a dependency of driver, which should be removed.
|
|
||||||
esp_ringbuf
|
|
||||||
# esp_timer is a dependency of freertos, esp_event, esp_wifi, driver.
|
# esp_timer is a dependency of freertos, esp_event, esp_wifi, driver.
|
||||||
# For freertos, it can be made a weak dependency conditional on FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
|
# For freertos, it can be made a weak dependency conditional on FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
|
||||||
esp_timer
|
esp_timer
|
||||||
|
|
Ładowanie…
Reference in New Issue