2018-06-08 03:32:43 -04:00
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
2017-03-31 03:05:25 -04:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
# include <string.h>
2019-06-05 22:57:29 -04:00
# include "sdkconfig.h"
2019-04-18 10:13:05 -04:00
# include <hal/spi_ll.h>
# include <hal/spi_slave_hal.h>
# include <soc/lldesc.h>
2019-09-03 02:06:26 -04:00
# include "driver/spi_common_internal.h"
2017-03-31 03:05:25 -04:00
# include "driver/spi_slave.h"
2018-06-08 03:32:43 -04:00
# include "soc/spi_periph.h"
2020-06-19 00:00:58 -04:00
# include "soc/gpio_caps.h"
2017-03-31 03:05:25 -04:00
# include "esp_types.h"
# include "esp_attr.h"
2019-03-26 04:30:43 -04:00
# include "esp_intr_alloc.h"
2017-03-31 03:05:25 -04:00
# include "esp_log.h"
# include "esp_err.h"
2017-09-24 03:05:35 -04:00
# include "esp_pm.h"
2017-03-31 03:05:25 -04:00
# include "freertos/FreeRTOS.h"
# include "freertos/semphr.h"
# include "freertos/xtensa_api.h"
# include "freertos/task.h"
2017-05-05 00:16:02 -04:00
# include "soc/soc_memory_layout.h"
2017-03-31 03:05:25 -04:00
# include "driver/gpio.h"
2017-05-03 04:03:28 -04:00
# include "esp_heap_caps.h"
2020-06-19 00:00:58 -04:00
# include "esp_rom_gpio.h"
2020-07-21 01:07:34 -04:00
# include "esp_rom_sys.h"
2020-09-14 05:33:10 -04:00
# include "hal/spi_slave_hal.h"
2019-06-13 02:19:31 -04:00
2017-03-31 03:05:25 -04:00
static const char * SPI_TAG = " spi_slave " ;
# define SPI_CHECK(a, str, ret_val) \
if ( ! ( a ) ) { \
ESP_LOGE ( SPI_TAG , " %s(%d): %s " , __FUNCTION__ , __LINE__ , str ) ; \
return ( ret_val ) ; \
}
2018-08-20 06:30:40 -04:00
# ifdef CONFIG_SPI_SLAVE_ISR_IN_IRAM
# define SPI_SLAVE_ISR_ATTR IRAM_ATTR
# else
# define SPI_SLAVE_ISR_ATTR
# endif
# ifdef CONFIG_SPI_SLAVE_IN_IRAM
# define SPI_SLAVE_ATTR IRAM_ATTR
# else
# define SPI_SLAVE_ATTR
# endif
2017-03-31 03:05:25 -04:00
typedef struct {
2018-09-06 02:47:45 -04:00
int id ;
2017-03-31 03:05:25 -04:00
spi_slave_interface_config_t cfg ;
intr_handle_t intr ;
2019-04-18 10:13:05 -04:00
spi_slave_hal_context_t hal ;
2017-03-31 03:05:25 -04:00
spi_slave_transaction_t * cur_trans ;
2018-03-21 08:42:45 -04:00
uint32_t flags ;
2017-03-31 03:05:25 -04:00
int max_transfer_sz ;
QueueHandle_t trans_queue ;
QueueHandle_t ret_queue ;
int dma_chan ;
2017-09-24 03:05:35 -04:00
# ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock ;
# endif
2017-03-31 03:05:25 -04:00
} spi_slave_t ;
2019-06-13 02:12:54 -04:00
static spi_slave_t * spihost [ SOC_SPI_PERIPH_NUM ] ;
2017-03-31 03:05:25 -04:00
static void IRAM_ATTR spi_intr ( void * arg ) ;
2019-10-24 07:00:26 -04:00
static inline bool is_valid_host ( spi_host_device_t host )
{
# if CONFIG_IDF_TARGET_ESP32
return host > = SPI1_HOST & & host < = SPI3_HOST ;
2020-07-29 01:13:51 -04:00
# elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
2019-10-24 07:00:26 -04:00
// SPI_HOST (SPI1_HOST) is not supported by the SPI Slave driver on ESP32-S2
return host > = SPI2_HOST & & host < = SPI3_HOST ;
# endif
}
2018-09-06 02:47:45 -04:00
static inline bool bus_is_iomux ( spi_slave_t * host )
{
2019-06-13 02:12:54 -04:00
return host - > flags & SPICOMMON_BUSFLAG_IOMUX_PINS ;
2018-09-06 02:47:45 -04:00
}
static void freeze_cs ( spi_slave_t * host )
{
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_in_signal ( GPIO_MATRIX_CONST_ONE_INPUT , spi_periph_signal [ host - > id ] . spics_in , false ) ;
2018-09-06 02:47:45 -04:00
}
// Use this function instead of cs_initial to avoid overwrite the output config
// This is used in test by internal gpio matrix connections
static inline void restore_cs ( spi_slave_t * host )
{
if ( bus_is_iomux ( host ) ) {
gpio_iomux_in ( host - > cfg . spics_io_num , spi_periph_signal [ host - > id ] . spics_in ) ;
} else {
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_in_signal ( host - > cfg . spics_io_num , spi_periph_signal [ host - > id ] . spics_in , false ) ;
2018-09-06 02:47:45 -04:00
}
}
2017-04-12 23:14:35 -04:00
esp_err_t spi_slave_initialize ( spi_host_device_t host , const spi_bus_config_t * bus_config , const spi_slave_interface_config_t * slave_config , int dma_chan )
2017-03-31 03:05:25 -04:00
{
2018-03-21 08:42:45 -04:00
bool spi_chan_claimed , dma_chan_claimed ;
2018-03-21 08:42:45 -04:00
esp_err_t ret = ESP_OK ;
esp_err_t err ;
2017-03-31 03:05:25 -04:00
//We only support HSPI/VSPI, period.
2019-10-24 07:00:26 -04:00
SPI_CHECK ( is_valid_host ( host ) , " invalid host " , ESP_ERR_INVALID_ARG ) ;
2019-06-13 02:19:31 -04:00
# if defined(CONFIG_IDF_TARGET_ESP32)
2017-09-22 05:47:06 -04:00
SPI_CHECK ( dma_chan > = 0 & & dma_chan < = 2 , " invalid dma channel " , ESP_ERR_INVALID_ARG ) ;
2020-01-16 22:47:08 -05:00
# elif defined(CONFIG_IDF_TARGET_ESP32S2)
2019-06-13 02:19:31 -04:00
SPI_CHECK ( dma_chan = = 0 | | dma_chan = = host , " invalid dma channel " , ESP_ERR_INVALID_ARG ) ;
# endif
2018-10-23 04:57:32 -04:00
SPI_CHECK ( ( bus_config - > intr_flags & ( ESP_INTR_FLAG_HIGH | ESP_INTR_FLAG_EDGE | ESP_INTR_FLAG_INTRDISABLED ) ) = = 0 , " intr flag not allowed " , ESP_ERR_INVALID_ARG ) ;
# ifndef CONFIG_SPI_SLAVE_ISR_IN_IRAM
SPI_CHECK ( ( bus_config - > intr_flags & ESP_INTR_FLAG_IRAM ) = = 0 , " ESP_INTR_FLAG_IRAM should be disabled when CONFIG_SPI_SLAVE_ISR_IN_IRAM is not set. " , ESP_ERR_INVALID_ARG ) ;
# endif
2017-09-22 05:47:06 -04:00
2018-10-05 03:39:32 -04:00
spi_chan_claimed = spicommon_periph_claim ( host , " spi slave " ) ;
2017-09-22 05:47:06 -04:00
SPI_CHECK ( spi_chan_claimed , " host already in use " , ESP_ERR_INVALID_STATE ) ;
2018-08-20 06:30:40 -04:00
2019-04-18 10:13:05 -04:00
bool use_dma = dma_chan ! = 0 ;
if ( use_dma ) {
2017-09-22 05:47:06 -04:00
dma_chan_claimed = spicommon_dma_chan_claim ( dma_chan ) ;
if ( ! dma_chan_claimed ) {
spicommon_periph_free ( host ) ;
SPI_CHECK ( dma_chan_claimed , " dma channel already in use " , ESP_ERR_INVALID_STATE ) ;
}
}
2017-03-31 03:05:25 -04:00
2017-04-26 23:24:44 -04:00
spihost [ host ] = malloc ( sizeof ( spi_slave_t ) ) ;
2018-03-21 08:42:45 -04:00
if ( spihost [ host ] = = NULL ) {
ret = ESP_ERR_NO_MEM ;
goto cleanup ;
}
2017-03-31 03:05:25 -04:00
memset ( spihost [ host ] , 0 , sizeof ( spi_slave_t ) ) ;
memcpy ( & spihost [ host ] - > cfg , slave_config , sizeof ( spi_slave_interface_config_t ) ) ;
2018-09-06 02:47:45 -04:00
spihost [ host ] - > id = host ;
2017-03-31 03:05:25 -04:00
2018-03-21 08:42:45 -04:00
err = spicommon_bus_initialize_io ( host , bus_config , dma_chan , SPICOMMON_BUSFLAG_SLAVE | bus_config - > flags , & spihost [ host ] - > flags ) ;
2018-03-21 08:42:45 -04:00
if ( err ! = ESP_OK ) {
ret = err ;
goto cleanup ;
}
2018-09-06 02:47:45 -04:00
spicommon_cs_initialize ( host , slave_config - > spics_io_num , 0 , ! bus_is_iomux ( spihost [ host ] ) ) ;
// The slave DMA suffers from unexpected transactions. Forbid reading if DMA is enabled by disabling the CS line.
2019-04-18 10:13:05 -04:00
if ( use_dma ) freeze_cs ( spihost [ host ] ) ;
2018-09-06 02:47:45 -04:00
2019-04-18 10:13:05 -04:00
int dma_desc_ct = 0 ;
2017-04-26 23:24:44 -04:00
spihost [ host ] - > dma_chan = dma_chan ;
2019-04-18 10:13:05 -04:00
if ( use_dma ) {
2017-03-31 03:05:25 -04:00
//See how many dma descriptors we need and allocate them
2019-04-18 10:13:05 -04:00
dma_desc_ct = ( bus_config - > max_transfer_sz + SPI_MAX_DMA_LEN - 1 ) / SPI_MAX_DMA_LEN ;
2017-04-26 23:24:44 -04:00
if ( dma_desc_ct = = 0 ) dma_desc_ct = 1 ; //default to 4k when max is not given
spihost [ host ] - > max_transfer_sz = dma_desc_ct * SPI_MAX_DMA_LEN ;
2017-03-31 03:05:25 -04:00
} else {
//We're limited to non-DMA transfers: the SPI work registers can hold 64 bytes at most.
2019-06-13 02:12:54 -04:00
spihost [ host ] - > max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE ;
2017-03-31 03:05:25 -04:00
}
2017-09-24 03:05:35 -04:00
# ifdef CONFIG_PM_ENABLE
2018-03-21 08:42:45 -04:00
err = esp_pm_lock_create ( ESP_PM_APB_FREQ_MAX , 0 , " spi_slave " ,
2017-09-24 03:05:35 -04:00
& spihost [ host ] - > pm_lock ) ;
if ( err ! = ESP_OK ) {
2018-03-21 08:42:45 -04:00
ret = err ;
goto cleanup ;
2017-09-24 03:05:35 -04:00
}
// Lock APB frequency while SPI slave driver is in use
esp_pm_lock_acquire ( spihost [ host ] - > pm_lock ) ;
# endif //CONFIG_PM_ENABLE
2017-03-31 03:05:25 -04:00
//Create queues
2017-04-26 23:24:44 -04:00
spihost [ host ] - > trans_queue = xQueueCreate ( slave_config - > queue_size , sizeof ( spi_slave_transaction_t * ) ) ;
spihost [ host ] - > ret_queue = xQueueCreate ( slave_config - > queue_size , sizeof ( spi_slave_transaction_t * ) ) ;
2018-03-21 08:42:45 -04:00
if ( ! spihost [ host ] - > trans_queue | | ! spihost [ host ] - > ret_queue ) {
ret = ESP_ERR_NO_MEM ;
goto cleanup ;
}
2017-03-31 03:05:25 -04:00
2018-10-23 04:57:32 -04:00
int flags = bus_config - > intr_flags | ESP_INTR_FLAG_INTRDISABLED ;
2018-08-20 06:30:40 -04:00
err = esp_intr_alloc ( spicommon_irqsource_for_host ( host ) , flags , spi_intr , ( void * ) spihost [ host ] , & spihost [ host ] - > intr ) ;
2018-03-21 08:42:45 -04:00
if ( err ! = ESP_OK ) {
ret = err ;
goto cleanup ;
}
2018-06-25 00:34:31 -04:00
2019-04-18 10:13:05 -04:00
spi_slave_hal_context_t * hal = & spihost [ host ] - > hal ;
2020-09-14 05:33:10 -04:00
//assign the SPI, RX DMA and TX DMA peripheral registers beginning address
spi_slave_hal_config_t hal_config = {
. host_id = host ,
. dma_in = SPI_LL_GET_HW ( host ) ,
. dma_out = SPI_LL_GET_HW ( host )
} ;
spi_slave_hal_init ( hal , & hal_config ) ;
2019-04-18 10:13:05 -04:00
if ( dma_desc_ct ) {
hal - > dmadesc_tx = heap_caps_malloc ( sizeof ( lldesc_t ) * dma_desc_ct , MALLOC_CAP_DMA ) ;
hal - > dmadesc_rx = heap_caps_malloc ( sizeof ( lldesc_t ) * dma_desc_ct , MALLOC_CAP_DMA ) ;
if ( ! hal - > dmadesc_tx | | ! hal - > dmadesc_rx ) {
ret = ESP_ERR_NO_MEM ;
goto cleanup ;
2018-06-25 00:34:31 -04:00
}
2017-03-31 03:05:25 -04:00
}
2019-04-18 10:13:05 -04:00
hal - > dmadesc_n = dma_desc_ct ;
hal - > rx_lsbfirst = ( slave_config - > flags & SPI_SLAVE_RXBIT_LSBFIRST ) ? 1 : 0 ;
hal - > tx_lsbfirst = ( slave_config - > flags & SPI_SLAVE_TXBIT_LSBFIRST ) ? 1 : 0 ;
hal - > mode = slave_config - > mode ;
hal - > use_dma = use_dma ;
2017-03-31 03:05:25 -04:00
2019-04-18 10:13:05 -04:00
spi_slave_hal_setup_device ( hal ) ;
2017-03-31 03:05:25 -04:00
return ESP_OK ;
2018-03-21 08:42:45 -04:00
cleanup :
2017-03-31 03:05:25 -04:00
if ( spihost [ host ] ) {
if ( spihost [ host ] - > trans_queue ) vQueueDelete ( spihost [ host ] - > trans_queue ) ;
if ( spihost [ host ] - > ret_queue ) vQueueDelete ( spihost [ host ] - > ret_queue ) ;
2019-04-18 10:13:05 -04:00
free ( spihost [ host ] - > hal . dmadesc_tx ) ;
free ( spihost [ host ] - > hal . dmadesc_rx ) ;
2017-09-24 03:05:35 -04:00
# ifdef CONFIG_PM_ENABLE
if ( spihost [ host ] - > pm_lock ) {
esp_pm_lock_release ( spihost [ host ] - > pm_lock ) ;
esp_pm_lock_delete ( spihost [ host ] - > pm_lock ) ;
}
# endif
2017-03-31 03:05:25 -04:00
}
2019-04-18 10:13:05 -04:00
spi_slave_hal_deinit ( & spihost [ host ] - > hal ) ;
2017-03-31 03:05:25 -04:00
free ( spihost [ host ] ) ;
2017-04-26 23:24:44 -04:00
spihost [ host ] = NULL ;
2017-03-31 03:05:25 -04:00
spicommon_periph_free ( host ) ;
2019-11-26 00:11:48 -05:00
if ( dma_chan ! = 0 ) spicommon_dma_chan_free ( dma_chan ) ;
2018-03-21 08:42:45 -04:00
return ret ;
2017-03-31 03:05:25 -04:00
}
esp_err_t spi_slave_free ( spi_host_device_t host )
{
2019-10-24 07:00:26 -04:00
SPI_CHECK ( is_valid_host ( host ) , " invalid host " , ESP_ERR_INVALID_ARG ) ;
2017-03-31 03:05:25 -04:00
SPI_CHECK ( spihost [ host ] , " host not slave " , ESP_ERR_INVALID_ARG ) ;
if ( spihost [ host ] - > trans_queue ) vQueueDelete ( spihost [ host ] - > trans_queue ) ;
if ( spihost [ host ] - > ret_queue ) vQueueDelete ( spihost [ host ] - > ret_queue ) ;
2017-09-22 05:47:06 -04:00
if ( spihost [ host ] - > dma_chan > 0 ) {
spicommon_dma_chan_free ( spihost [ host ] - > dma_chan ) ;
}
2019-04-18 10:13:05 -04:00
free ( spihost [ host ] - > hal . dmadesc_tx ) ;
free ( spihost [ host ] - > hal . dmadesc_rx ) ;
2017-09-30 06:26:55 -04:00
esp_intr_free ( spihost [ host ] - > intr ) ;
2017-09-24 03:05:35 -04:00
# ifdef CONFIG_PM_ENABLE
esp_pm_lock_release ( spihost [ host ] - > pm_lock ) ;
esp_pm_lock_delete ( spihost [ host ] - > pm_lock ) ;
# endif //CONFIG_PM_ENABLE
2017-03-31 03:05:25 -04:00
free ( spihost [ host ] ) ;
2017-04-26 23:24:44 -04:00
spihost [ host ] = NULL ;
2017-03-31 03:05:25 -04:00
spicommon_periph_free ( host ) ;
return ESP_OK ;
}
2018-08-20 06:30:40 -04:00
esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans ( spi_host_device_t host , const spi_slave_transaction_t * trans_desc , TickType_t ticks_to_wait )
2017-03-31 03:05:25 -04:00
{
BaseType_t r ;
2019-10-24 07:00:26 -04:00
SPI_CHECK ( is_valid_host ( host ) , " invalid host " , ESP_ERR_INVALID_ARG ) ;
2017-03-31 03:05:25 -04:00
SPI_CHECK ( spihost [ host ] , " host not slave " , ESP_ERR_INVALID_ARG ) ;
2018-08-20 06:30:40 -04:00
SPI_CHECK ( spihost [ host ] - > dma_chan = = 0 | | trans_desc - > tx_buffer = = NULL | | esp_ptr_dma_capable ( trans_desc - > tx_buffer ) ,
2017-05-04 23:55:19 -04:00
" txdata not in DMA-capable memory " , ESP_ERR_INVALID_ARG ) ;
2018-11-19 22:13:44 -05:00
SPI_CHECK ( spihost [ host ] - > dma_chan = = 0 | | trans_desc - > rx_buffer = = NULL | |
( esp_ptr_dma_capable ( trans_desc - > rx_buffer ) & & esp_ptr_word_aligned ( trans_desc - > rx_buffer ) & &
( trans_desc - > length % 4 = = 0 ) ) ,
" rxdata not in DMA-capable memory or not WORD aligned " , ESP_ERR_INVALID_ARG ) ;
2017-03-31 03:05:25 -04:00
2017-04-26 23:24:44 -04:00
SPI_CHECK ( trans_desc - > length < = spihost [ host ] - > max_transfer_sz * 8 , " data transfer > host maximum " , ESP_ERR_INVALID_ARG ) ;
r = xQueueSend ( spihost [ host ] - > trans_queue , ( void * ) & trans_desc , ticks_to_wait ) ;
2017-03-31 03:05:25 -04:00
if ( ! r ) return ESP_ERR_TIMEOUT ;
esp_intr_enable ( spihost [ host ] - > intr ) ;
return ESP_OK ;
}
2018-08-20 06:30:40 -04:00
esp_err_t SPI_SLAVE_ATTR spi_slave_get_trans_result ( spi_host_device_t host , spi_slave_transaction_t * * trans_desc , TickType_t ticks_to_wait )
2017-03-31 03:05:25 -04:00
{
BaseType_t r ;
2019-10-24 07:00:26 -04:00
SPI_CHECK ( is_valid_host ( host ) , " invalid host " , ESP_ERR_INVALID_ARG ) ;
2017-03-31 03:05:25 -04:00
SPI_CHECK ( spihost [ host ] , " host not slave " , ESP_ERR_INVALID_ARG ) ;
2017-04-26 23:24:44 -04:00
r = xQueueReceive ( spihost [ host ] - > ret_queue , ( void * ) trans_desc , ticks_to_wait ) ;
2017-03-31 03:05:25 -04:00
if ( ! r ) return ESP_ERR_TIMEOUT ;
return ESP_OK ;
}
2018-08-20 06:30:40 -04:00
esp_err_t SPI_SLAVE_ATTR spi_slave_transmit ( spi_host_device_t host , spi_slave_transaction_t * trans_desc , TickType_t ticks_to_wait )
2017-03-31 03:05:25 -04:00
{
esp_err_t ret ;
spi_slave_transaction_t * ret_trans ;
//ToDo: check if any spi transfers in flight
2017-04-26 23:24:44 -04:00
ret = spi_slave_queue_trans ( host , trans_desc , ticks_to_wait ) ;
if ( ret ! = ESP_OK ) return ret ;
ret = spi_slave_get_trans_result ( host , & ret_trans , ticks_to_wait ) ;
if ( ret ! = ESP_OK ) return ret ;
assert ( ret_trans = = trans_desc ) ;
2017-03-31 03:05:25 -04:00
return ESP_OK ;
}
# ifdef DEBUG_SLAVE
2017-04-26 23:24:44 -04:00
static void dumpregs ( spi_dev_t * hw )
{
2020-07-21 01:07:34 -04:00
esp_rom_printf ( " ***REG DUMP *** \n " ) ;
esp_rom_printf ( " mosi_dlen : %08X \n " , hw - > mosi_dlen . val ) ;
esp_rom_printf ( " miso_dlen : %08X \n " , hw - > miso_dlen . val ) ;
esp_rom_printf ( " slv_wrbuf_dlen : %08X \n " , hw - > slv_wrbuf_dlen . val ) ;
esp_rom_printf ( " slv_rdbuf_dlen : %08X \n " , hw - > slv_rdbuf_dlen . val ) ;
esp_rom_printf ( " slave : %08X \n " , hw - > slave . val ) ;
esp_rom_printf ( " slv_rdata_bit : %x \n " , hw - > slv_rd_bit . slv_rdata_bit ) ;
esp_rom_printf ( " dma_rx_status : %08X \n " , hw - > dma_rx_status ) ;
esp_rom_printf ( " dma_tx_status : %08X \n " , hw - > dma_tx_status ) ;
2017-03-31 03:05:25 -04:00
}
2017-04-26 23:24:44 -04:00
static void dumpll ( lldesc_t * ll )
{
2020-07-21 01:07:34 -04:00
esp_rom_printf ( " ****LL DUMP**** \n " ) ;
esp_rom_printf ( " Size %d \n " , ll - > size ) ;
esp_rom_printf ( " Len: %d \n " , ll - > length ) ;
esp_rom_printf ( " Owner: %s \n " , ll - > owner ? " dma " : " cpu " ) ;
2017-03-31 03:05:25 -04:00
}
# endif
2018-08-20 06:30:40 -04:00
static void SPI_SLAVE_ISR_ATTR spi_slave_restart_after_dmareset ( void * arg )
2017-03-31 03:05:25 -04:00
{
2017-04-26 23:24:44 -04:00
spi_slave_t * host = ( spi_slave_t * ) arg ;
2017-03-31 03:05:25 -04:00
esp_intr_enable ( host - > intr ) ;
}
//This is run in interrupt context and apart from initialization and destruction, this is the only code
//touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are
//no muxes in this code.
2018-08-20 06:30:40 -04:00
static void SPI_SLAVE_ISR_ATTR spi_intr ( void * arg )
2017-03-31 03:05:25 -04:00
{
BaseType_t r ;
2017-04-26 23:24:44 -04:00
BaseType_t do_yield = pdFALSE ;
spi_slave_transaction_t * trans = NULL ;
spi_slave_t * host = ( spi_slave_t * ) arg ;
2019-04-18 10:13:05 -04:00
spi_slave_hal_context_t * hal = & host - > hal ;
2017-03-31 03:05:25 -04:00
# ifdef DEBUG_SLAVE
dumpregs ( host - > hw ) ;
if ( host - > dmadesc_rx ) dumpll ( & host - > dmadesc_rx [ 0 ] ) ;
# endif
2019-04-18 10:13:05 -04:00
assert ( spi_slave_hal_usr_is_done ( hal ) ) ;
2017-03-31 03:05:25 -04:00
2019-04-18 10:13:05 -04:00
bool use_dma = host - > dma_chan ! = 0 ;
2017-03-31 03:05:25 -04:00
if ( host - > cur_trans ) {
2018-09-06 02:47:45 -04:00
// When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready.
2019-04-18 10:13:05 -04:00
if ( use_dma ) freeze_cs ( host ) ;
spi_slave_hal_store_result ( hal ) ;
host - > cur_trans - > trans_len = spi_slave_hal_get_rcv_bitlen ( hal ) ;
2017-09-30 07:59:05 -04:00
2019-04-18 10:13:05 -04:00
if ( spi_slave_hal_dma_need_reset ( hal ) ) {
spicommon_dmaworkaround_req_reset ( host - > dma_chan , spi_slave_restart_after_dmareset , host ) ;
2017-03-31 03:05:25 -04:00
}
if ( host - > cfg . post_trans_cb ) host - > cfg . post_trans_cb ( host - > cur_trans ) ;
//Okay, transaction is done.
//Return transaction descriptor.
xQueueSendFromISR ( host - > ret_queue , & host - > cur_trans , & do_yield ) ;
2017-04-26 23:24:44 -04:00
host - > cur_trans = NULL ;
2017-03-31 03:05:25 -04:00
}
2019-04-18 10:13:05 -04:00
if ( use_dma ) {
2017-03-31 03:05:25 -04:00
spicommon_dmaworkaround_idle ( host - > dma_chan ) ;
if ( spicommon_dmaworkaround_reset_in_progress ( ) ) {
//We need to wait for the reset to complete. Disable int (will be re-enabled on reset callback) and exit isr.
esp_intr_disable ( host - > intr ) ;
if ( do_yield ) portYIELD_FROM_ISR ( ) ;
return ;
}
}
2019-05-31 03:23:10 -04:00
//Disable interrupt before checking to avoid concurrency issue.
esp_intr_disable ( host - > intr ) ;
2017-03-31 03:05:25 -04:00
//Grab next transaction
2017-04-26 23:24:44 -04:00
r = xQueueReceiveFromISR ( host - > trans_queue , & trans , & do_yield ) ;
2019-05-31 03:23:10 -04:00
if ( r ) {
//enable the interrupt again if there is packet to send
esp_intr_enable ( host - > intr ) ;
2017-03-31 03:05:25 -04:00
//We have a transaction. Send it.
2017-04-26 23:24:44 -04:00
host - > cur_trans = trans ;
2017-03-31 03:05:25 -04:00
2019-04-18 10:13:05 -04:00
hal - > bitlen = trans - > length ;
hal - > rx_buffer = trans - > rx_buffer ;
hal - > tx_buffer = trans - > tx_buffer ;
if ( use_dma ) {
2017-03-31 03:05:25 -04:00
spicommon_dmaworkaround_transfer_active ( host - > dma_chan ) ;
}
2019-04-18 10:13:05 -04:00
spi_slave_hal_prepare_data ( hal ) ;
2017-03-31 03:05:25 -04:00
2018-09-06 02:47:45 -04:00
//The slave rx dma get disturbed by unexpected transaction. Only connect the CS when slave is ready.
2019-04-18 10:13:05 -04:00
if ( use_dma ) {
restore_cs ( host ) ;
}
2018-09-06 02:47:45 -04:00
2017-03-31 03:05:25 -04:00
//Kick off transfer
2019-04-18 10:13:05 -04:00
spi_slave_hal_user_start ( hal ) ;
2017-03-31 03:05:25 -04:00
if ( host - > cfg . post_setup_cb ) host - > cfg . post_setup_cb ( trans ) ;
}
if ( do_yield ) portYIELD_FROM_ISR ( ) ;
}