| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778 |
- /*
- * SPDX-FileCopyrightText: 2010-2023 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- // Internal header, don't use it in the user code
- #pragma once
- #include <esp_intr_alloc.h>
- #include "driver/spi_common.h"
- #include "freertos/FreeRTOS.h"
- #include "hal/spi_types.h"
- #include "hal/dma_types.h"
- #include "soc/gdma_channel.h"
- #include "esp_pm.h"
- #if SOC_GDMA_SUPPORTED
- #include "esp_private/gdma.h"
- #endif
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- #ifdef CONFIG_SPI_MASTER_ISR_IN_IRAM
- #define SPI_MASTER_ISR_ATTR IRAM_ATTR
- #else
- #define SPI_MASTER_ISR_ATTR
- #endif
- #ifdef CONFIG_SPI_MASTER_IN_IRAM
- #define SPI_MASTER_ATTR IRAM_ATTR
- #else
- #define SPI_MASTER_ATTR
- #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
- #define DMA_DESC_MEM_ALIGN_SIZE 4
- #define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel
- typedef dma_descriptor_align4_t spi_dma_desc_t;
- #else
- #define DMA_DESC_MEM_ALIGN_SIZE 8
- #define SPI_GDMA_NEW_CHANNEL gdma_new_axi_channel
- typedef dma_descriptor_align8_t spi_dma_desc_t;
- #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
- typedef struct {
- spi_bus_config_t bus_cfg; ///< Config used to initialize the bus
- uint32_t flags; ///< Flags (attributes) of the bus
- int max_transfer_sz; ///< Maximum length of bytes available to send
- bool dma_enabled; ///< To enable DMA or not
- uint16_t internal_mem_align_size; ///< Buffer align byte requirement for internal memory
- int tx_dma_chan; ///< TX DMA channel, on ESP32 and ESP32S2, tx_dma_chan and rx_dma_chan are same
- int rx_dma_chan; ///< RX DMA channel, on ESP32 and ESP32S2, tx_dma_chan and rx_dma_chan are same
- int dma_desc_num; ///< DMA descriptor number of dmadesc_tx or dmadesc_rx.
- spi_dma_desc_t *dmadesc_tx; ///< DMA descriptor array for TX
- spi_dma_desc_t *dmadesc_rx; ///< DMA descriptor array for RX
- spi_bus_lock_handle_t lock;
- #ifdef CONFIG_PM_ENABLE
- esp_pm_lock_handle_t pm_lock; ///< Power management lock
- #endif
- } spi_bus_attr_t;
- /// Destructor called when a bus is deinitialized.
- 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
- *
- * @param host_id SPI host ID
- * @param dma_chan DMA channel to be used
- * @param[out] out_actual_tx_dma_chan Actual TX DMA channel (if you choose to assign a specific DMA channel, this will be the channel you assigned before)
- * @param[out] out_actual_rx_dma_chan Actual RX DMA channel (if you choose to assign a specific DMA channel, this will be the channel you assigned before)
- *
- * @return
- * - ESP_OK: On success
- * - ESP_ERR_NO_MEM: No enough memory
- * - ESP_ERR_NOT_FOUND: There is no available DMA channel
- */
- esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan, uint32_t *out_actual_tx_dma_chan, uint32_t *out_actual_rx_dma_chan);
- /**
- * @brief Free DMA for SPI
- *
- * @param host_id SPI host ID
- *
- * @return
- * - ESP_OK: On success
- */
- esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id);
- #if SOC_GDMA_SUPPORTED
- /**
- * @brief Get SPI GDMA Handle for GMDA Supported Chip
- *
- * @param host_id SPI host ID
- * @param gdma_handle GDMA Handle to Return
- * @param gdma_direction GDMA Channel Direction in Enum
- * - GDMA_CHANNEL_DIRECTION_TX
- * - GDMA_CHANNEL_DIRECTION_RX
- *
- * @return
- * - ESP_OK: On success
- */
- esp_err_t spicommon_gdma_get_handle(spi_host_device_t host_id, gdma_channel_handle_t *gdma_handle, gdma_channel_direction_t gdma_direction);
- #endif
- /**
- * @brief Connect a SPI peripheral to GPIO pins
- *
- * This routine is used to connect a SPI peripheral to the IO-pads and DMA channel given in
- * the arguments. Depending on the IO-pads requested, the routing is done either using the
- * IO_mux or using the GPIO matrix.
- *
- * @param host SPI peripheral to be routed
- * @param bus_config Pointer to a spi_bus_config struct detailing the GPIO pins
- * @param flags Combination of SPICOMMON_BUSFLAG_* flags, set to ensure the pins set are capable with some functions:
- * - ``SPICOMMON_BUSFLAG_MASTER``: Initialize I/O in master mode
- * - ``SPICOMMON_BUSFLAG_SLAVE``: Initialize I/O in slave mode
- * - ``SPICOMMON_BUSFLAG_IOMUX_PINS``: Pins set should match the iomux pins of the controller.
- * - ``SPICOMMON_BUSFLAG_SCLK``, ``SPICOMMON_BUSFLAG_MISO``, ``SPICOMMON_BUSFLAG_MOSI``:
- * Make sure SCLK/MISO/MOSI is/are set to a valid GPIO. Also check output capability according to the mode.
- * - ``SPICOMMON_BUSFLAG_DUAL``: Make sure both MISO and MOSI are output capable so that DIO mode is capable.
- * - ``SPICOMMON_BUSFLAG_WPHD`` Make sure WP and HD are set to valid output GPIOs.
- * - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``.
- * - ``SPICOMMON_BUSFLAG_IO4_IO7``: Make sure spi data4 ~ spi data7 are set to valid output GPIOs.
- * - ``SPICOMMON_BUSFLAG_OCTAL``: Combination of ``SPICOMMON_BUSFLAG_QUAL`` and ``SPICOMMON_BUSFLAG_IO4_IO7``.
- * @param[out] flags_o A SPICOMMON_BUSFLAG_* flag combination of bus abilities will be written to this address.
- * Leave to NULL if not needed.
- * - ``SPICOMMON_BUSFLAG_IOMUX_PINS``: The bus is connected to iomux pins.
- * - ``SPICOMMON_BUSFLAG_SCLK``, ``SPICOMMON_BUSFLAG_MISO``, ``SPICOMMON_BUSFLAG_MOSI``: The bus has
- * CLK/MISO/MOSI connected.
- * - ``SPICOMMON_BUSFLAG_DUAL``: The bus is capable with DIO mode.
- * - ``SPICOMMON_BUSFLAG_WPHD`` The bus has WP and HD connected.
- * - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``.
- * - ``SPICOMMON_BUSFLAG_IO4_IO7``: The bus has spi data4 ~ spi data7 connected.
- * - ``SPICOMMON_BUSFLAG_OCTAL``: Combination of ``SPICOMMON_BUSFLAG_QUAL`` and ``SPICOMMON_BUSFLAG_IO4_IO7``.
- * @return
- * - ESP_ERR_INVALID_ARG if parameter is invalid
- * - ESP_OK on success
- */
- esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o);
- /**
- * @brief Free the IO used by a SPI peripheral
- *
- * @param bus_cfg Bus config struct which defines which pins to be used.
- *
- * @return
- * - ESP_ERR_INVALID_ARG if parameter is invalid
- * - ESP_OK on success
- */
- esp_err_t spicommon_bus_free_io_cfg(const spi_bus_config_t *bus_cfg);
- /**
- * @brief Initialize a Chip Select pin for a specific SPI peripheral
- *
- * @param host SPI peripheral
- * @param cs_io_num GPIO pin to route
- * @param cs_num CS id to route
- * @param force_gpio_matrix If true, CS will always be routed through the GPIO matrix. If false,
- * if the GPIO number allows it, the routing will happen through the IO_mux.
- */
- void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_num, int force_gpio_matrix);
- /**
- * @brief Free a chip select line
- *
- * @param cs_gpio_num CS gpio num to free
- */
- void spicommon_cs_free_io(int cs_gpio_num);
- /**
- * @brief Check whether all pins used by a host are through IOMUX.
- *
- * @param host SPI peripheral
- *
- * @return false if any pins are through the GPIO matrix, otherwise true.
- */
- bool spicommon_bus_using_iomux(spi_host_device_t host);
- /**
- * @brief Get the IRQ source for a specific SPI host
- *
- * @param host The SPI host
- *
- * @return The hosts IRQ source
- */
- int spicommon_irqsource_for_host(spi_host_device_t host);
- /**
- * @brief Get the IRQ source for a specific SPI DMA
- *
- * @param host The SPI host
- *
- * @return The hosts IRQ source
- */
- int spicommon_irqdma_source_for_host(spi_host_device_t host);
- /**
- * Callback, to be called when a DMA engine reset is completed
- */
- typedef void(*dmaworkaround_cb_t)(void *arg);
- #if CONFIG_IDF_TARGET_ESP32
- //This workaround is only for esp32
- /**
- * @brief Request a reset for a certain DMA channel
- *
- * @note In some (well-defined) cases in the ESP32 (at least rev v.0 and v.1), a SPI DMA channel will get confused. This can be remedied
- * by resetting the SPI DMA hardware in case this happens. Unfortunately, the reset knob used for thsi will reset _both_ DMA channels, and
- * as such can only done safely when both DMA channels are idle. These functions coordinate this.
- *
- * Essentially, when a reset is needed, a driver can request this using spicommon_dmaworkaround_req_reset. This is supposed to be called
- * with an user-supplied function as an argument. If both DMA channels are idle, this call will reset the DMA subsystem and return true.
- * If the other DMA channel is still busy, it will return false; as soon as the other DMA channel is done, however, it will reset the
- * DMA subsystem and call the callback. The callback is then supposed to be used to continue the SPI drivers activity.
- *
- * @param dmachan DMA channel associated with the SPI host that needs a reset
- * @param cb Callback to call in case DMA channel cannot be reset immediately
- * @param arg Argument to the callback
- *
- * @return True when a DMA reset could be executed immediately. False when it could not; in this
- * case the callback will be called with the specified argument when the logic can execute
- * a reset, after that reset.
- */
- bool spicommon_dmaworkaround_req_reset(int dmachan, dmaworkaround_cb_t cb, void *arg);
- /**
- * @brief Check if a DMA reset is requested but has not completed yet
- *
- * @return True when a DMA reset is requested but hasn't completed yet. False otherwise.
- */
- bool spicommon_dmaworkaround_reset_in_progress(void);
- /**
- * @brief Mark a DMA channel as idle.
- *
- * A call to this function tells the workaround logic that this channel will
- * not be affected by a global SPI DMA reset.
- */
- void spicommon_dmaworkaround_idle(int dmachan);
- /**
- * @brief Mark a DMA channel as active.
- *
- * A call to this function tells the workaround logic that this channel will
- * be affected by a global SPI DMA reset, and a reset like that should not be attempted.
- */
- void spicommon_dmaworkaround_transfer_active(int dmachan);
- #endif //#if CONFIG_IDF_TARGET_ESP32
- /*******************************************************************************
- * Bus attributes
- ******************************************************************************/
- /**
- * @brief Set bus lock for the main bus, called by startup code.
- *
- * @param lock The lock to be used by the main SPI bus.
- */
- void spi_bus_main_set_lock(spi_bus_lock_handle_t lock);
- /**
- * @brief Get the attributes of a specified SPI bus.
- *
- * @param host_id The specified host to get attribute
- * @return (Const) Pointer to the attributes
- */
- const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id);
- /**
- * @brief Register a function to a initialized bus to make it called when deinitializing the bus.
- *
- * @param host_id The SPI bus to register the destructor.
- * @param f Destructor to register
- * @param arg The argument to call the destructor
- * @return Always ESP_OK.
- */
- esp_err_t spi_bus_register_destroy_func(spi_host_device_t host_id,
- 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
- }
- #endif
|