2019-06-13 02:12:54 -04:00
// Copyright 2015-2019 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
2019-06-13 02:12:54 -04:00
//
2017-03-31 03:05:25 -04:00
// 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"
2017-03-31 03:05:25 -04:00
# include "driver/spi_master.h"
2018-06-08 03:32:43 -04:00
# include "soc/spi_periph.h"
2017-03-31 03:05:25 -04:00
# include "esp_types.h"
# include "esp_attr.h"
# include "esp_log.h"
# include "esp_err.h"
# include "soc/soc.h"
2020-09-08 05:05:49 -04:00
# include "soc/soc_caps.h"
2019-01-23 04:07:03 -05:00
# include "soc/lldesc.h"
2017-03-31 03:05:25 -04:00
# include "driver/gpio.h"
# include "driver/periph_ctrl.h"
2017-05-03 04:03:28 -04:00
# include "esp_heap_caps.h"
2019-09-03 02:06:26 -04:00
# include "driver/spi_common_internal.h"
2018-01-30 22:15:23 -05:00
# include "stdatomic.h"
2019-01-23 04:07:03 -05:00
# include "hal/spi_hal.h"
2021-03-15 22:55:05 -04:00
# include "hal/gpio_hal.h"
2020-06-19 00:00:58 -04:00
# include "esp_rom_gpio.h"
2020-11-26 03:56:13 -05:00
# if CONFIG_IDF_TARGET_ESP32
# include "soc/dport_reg.h"
# endif
2020-09-08 05:05:49 -04:00
# if SOC_GDMA_SUPPORTED
2021-01-27 08:56:16 -05:00
# include "esp_private/gdma.h"
2020-09-08 05:05:49 -04:00
# endif
2019-10-24 07:00:26 -04:00
2017-03-31 03:05:25 -04:00
static const char * SPI_TAG = " spi " ;
2019-02-11 01:17:31 -05:00
# define SPI_CHECK(a, str, ret_val) do { \
2017-03-31 03:05:25 -04:00
if ( ! ( a ) ) { \
ESP_LOGE ( SPI_TAG , " %s(%d): %s " , __FUNCTION__ , __LINE__ , str ) ; \
return ( ret_val ) ; \
2019-02-11 01:17:31 -05:00
} \
} while ( 0 )
# define SPI_CHECK_PIN(pin_num, pin_name, check_output) if (check_output) { \
SPI_CHECK ( GPIO_IS_VALID_OUTPUT_GPIO ( pin_num ) , pin_name " not valid " , ESP_ERR_INVALID_ARG ) ; \
} else { \
SPI_CHECK ( GPIO_IS_VALID_GPIO ( pin_num ) , pin_name " not valid " , ESP_ERR_INVALID_ARG ) ; \
}
2017-03-31 03:05:25 -04:00
2021-02-07 01:39:07 -05:00
# define SPI_MAIN_BUS_DEFAULT() { \
2021-02-03 02:14:17 -05:00
. host_id = 0 , \
. bus_attr = { \
. tx_dma_chan = 0 , \
. rx_dma_chan = 0 , \
. max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE , \
. dma_desc_num = 0 , \
} , \
}
2017-03-31 03:05:25 -04:00
2018-04-24 03:34:07 -04:00
# define FUNC_GPIO PIN_FUNC_GPIO
2017-08-31 07:59:30 -04:00
2019-10-24 07:00:26 -04:00
typedef struct {
int host_id ;
spi_destroy_func_t destroy_func ;
void * destroy_arg ;
spi_bus_attr_t bus_attr ;
2021-01-27 08:56:16 -05:00
# if SOC_GDMA_SUPPORTED
gdma_channel_handle_t tx_channel ;
gdma_channel_handle_t rx_channel ;
# endif
2019-10-24 07:00:26 -04:00
} spicommon_bus_context_t ;
2017-03-31 03:05:25 -04:00
//Periph 1 is 'claimed' by SPI flash code.
2021-01-27 08:56:16 -05:00
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)
2021-02-03 02:14:17 -05:00
ATOMIC_VAR_INIT ( false ) ,
2019-06-13 02:19:31 -04:00
# endif
2019-06-13 02:12:54 -04:00
} ;
2021-02-03 02:14:17 -05:00
2018-10-05 03:39:32 -04:00
static const char * spi_claiming_func [ 3 ] = { NULL , NULL , NULL } ;
2021-02-07 01:39:07 -05:00
static spicommon_bus_context_t s_mainbus = SPI_MAIN_BUS_DEFAULT ( ) ;
2021-02-03 02:14:17 -05:00
static spicommon_bus_context_t * bus_ctx [ SOC_SPI_PERIPH_NUM ] = { & s_mainbus } ;
2021-01-27 08:56:16 -05:00
# if !SOC_GDMA_SUPPORTED
2021-02-03 02:14:17 -05:00
//Each bit stands for 1 dma channel, BIT(0) should be used for SPI1
2017-08-31 07:59:30 -04:00
static uint8_t spi_dma_chan_enabled = 0 ;
static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED ;
2021-02-03 02:14:17 -05:00
# endif //#if !SOC_GDMA_SUPPORTED
2017-08-31 07:59:30 -04:00
2019-10-24 07:00:26 -04:00
2021-02-03 02:14:17 -05:00
static inline bool is_valid_host ( spi_host_device_t host )
{
# if (SOC_SPI_PERIPH_NUM == 2)
return host > = SPI1_HOST & & host < = SPI2_HOST ;
# elif (SOC_SPI_PERIPH_NUM == 3)
return host > = SPI1_HOST & & host < = SPI3_HOST ;
2021-01-26 07:18:52 -05:00
# endif
2021-02-03 02:14:17 -05:00
}
2021-01-26 07:18:52 -05:00
2021-01-27 08:56:16 -05:00
//----------------------------------------------------------alloc spi periph-------------------------------------------------------//
2017-03-31 03:05:25 -04:00
//Returns true if this peripheral is successfully claimed, false if otherwise.
2018-10-05 03:39:32 -04:00
bool spicommon_periph_claim ( spi_host_device_t host , const char * source )
2017-04-26 23:24:44 -04:00
{
2018-01-30 22:15:23 -05:00
bool false_var = false ;
bool ret = atomic_compare_exchange_strong ( & spi_periph_claimed [ host ] , & false_var , true ) ;
2018-10-05 03:39:32 -04:00
if ( ret ) {
spi_claiming_func [ host ] = source ;
periph_module_enable ( spi_periph_signal [ host ] . module ) ;
} else {
ESP_EARLY_LOGE ( SPI_TAG , " SPI%d already claimed by %s. " , host + 1 , spi_claiming_func [ host ] ) ;
}
2017-03-31 03:05:25 -04:00
return ret ;
}
2018-10-05 03:39:32 -04:00
bool spicommon_periph_in_use ( spi_host_device_t host )
{
return atomic_load ( & spi_periph_claimed [ host ] ) ;
}
2017-03-31 03:05:25 -04:00
//Returns true if this peripheral is successfully freed, false if otherwise.
2017-04-26 23:24:44 -04:00
bool spicommon_periph_free ( spi_host_device_t host )
{
2018-01-30 22:15:23 -05:00
bool true_var = true ;
bool ret = atomic_compare_exchange_strong ( & spi_periph_claimed [ host ] , & true_var , false ) ;
2018-06-08 03:32:43 -04:00
if ( ret ) periph_module_disable ( spi_periph_signal [ host ] . module ) ;
2017-03-31 03:05:25 -04:00
return ret ;
}
2017-04-26 23:24:44 -04:00
int spicommon_irqsource_for_host ( spi_host_device_t host )
{
2018-06-08 03:32:43 -04:00
return spi_periph_signal [ host ] . irq ;
2017-03-31 03:05:25 -04:00
}
2019-06-13 02:12:54 -04:00
int spicommon_irqdma_source_for_host ( spi_host_device_t host )
{
return spi_periph_signal [ host ] . irq_dma ;
}
2021-02-03 02:14:17 -05:00
//----------------------------------------------------------alloc dma periph-------------------------------------------------------//
2021-01-27 08:56:16 -05:00
# if !SOC_GDMA_SUPPORTED
2020-11-26 03:56:13 -05:00
static inline periph_module_t get_dma_periph ( int dma_chan )
2019-06-13 02:12:54 -04:00
{
2021-02-03 02:14:17 -05:00
assert ( dma_chan > = 1 & & dma_chan < = SOC_SPI_DMA_CHAN_NUM ) ;
2020-01-16 22:47:08 -05:00
# if CONFIG_IDF_TARGET_ESP32S2
2019-12-26 02:25:24 -05:00
if ( dma_chan = = 1 ) {
2019-06-13 02:19:31 -04:00
return PERIPH_SPI2_DMA_MODULE ;
2021-02-03 02:14:17 -05:00
} else if ( dma_chan = = 2 ) {
2019-06-13 02:19:31 -04:00
return PERIPH_SPI3_DMA_MODULE ;
} else {
abort ( ) ;
}
2019-12-26 02:25:24 -05:00
# elif CONFIG_IDF_TARGET_ESP32
2019-06-13 02:12:54 -04:00
return PERIPH_SPI_DMA_MODULE ;
2019-06-13 02:19:31 -04:00
# endif
2019-06-13 02:12:54 -04:00
}
2021-02-19 22:03:28 -05:00
static bool spicommon_dma_chan_claim ( int dma_chan , uint32_t * out_actual_dma_chan )
2017-08-31 07:59:30 -04:00
{
bool ret = false ;
portENTER_CRITICAL ( & spi_dma_spinlock ) ;
2021-02-03 02:14:17 -05:00
bool is_used = ( BIT ( dma_chan ) & spi_dma_chan_enabled ) ;
if ( ! is_used ) {
spi_dma_chan_enabled | = BIT ( dma_chan ) ;
periph_module_enable ( get_dma_periph ( dma_chan ) ) ;
2021-02-19 22:03:28 -05:00
* out_actual_dma_chan = dma_chan ;
2017-08-31 07:59:30 -04:00
ret = true ;
}
portEXIT_CRITICAL ( & spi_dma_spinlock ) ;
return ret ;
}
2021-01-26 07:18:52 -05:00
static void spicommon_connect_spi_and_dma ( spi_host_device_t host , int dma_chan )
2020-09-08 05:05:49 -04:00
{
# if CONFIG_IDF_TARGET_ESP32
DPORT_SET_PERI_REG_BITS ( DPORT_SPI_DMA_CHAN_SEL_REG , 3 , dma_chan , ( host * 2 ) ) ;
# elif CONFIG_IDF_TARGET_ESP32S2
//On ESP32S2, each SPI controller has its own DMA channel. So there is no need to connect them.
2020-11-26 03:56:13 -05:00
# endif
2021-01-27 08:56:16 -05:00
}
2021-02-19 22:03:28 -05:00
static 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 )
2021-01-27 08:56:16 -05:00
{
2021-02-03 02:14:17 -05:00
assert ( is_valid_host ( host_id ) ) ;
# if CONFIG_IDF_TARGET_ESP32
2021-02-19 22:03:28 -05:00
assert ( dma_chan > SPI_DMA_DISABLED & & dma_chan < = SPI_DMA_CH_AUTO ) ;
2021-02-03 02:14:17 -05:00
# elif CONFIG_IDF_TARGET_ESP32S2
2021-02-19 22:03:28 -05:00
assert ( dma_chan = = ( int ) host_id | | dma_chan = = SPI_DMA_CH_AUTO ) ;
2021-01-27 08:56:16 -05:00
# endif
2020-09-08 05:05:49 -04:00
2021-01-27 08:56:16 -05:00
esp_err_t ret = ESP_OK ;
2021-02-03 02:14:17 -05:00
bool success = false ;
2021-02-19 22:03:28 -05:00
uint32_t actual_dma_chan = 0 ;
2021-01-26 07:18:52 -05:00
2021-02-19 22:03:28 -05:00
if ( dma_chan = = SPI_DMA_CH_AUTO ) {
2021-01-26 07:18:52 -05:00
# if CONFIG_IDF_TARGET_ESP32
2021-02-03 02:14:17 -05:00
for ( int i = 1 ; i < SOC_SPI_DMA_CHAN_NUM + 1 ; i + + ) {
2021-02-19 22:03:28 -05:00
success = spicommon_dma_chan_claim ( i , & actual_dma_chan ) ;
2021-02-03 02:14:17 -05:00
if ( success ) {
2021-01-26 07:18:52 -05:00
break ;
}
}
# elif CONFIG_IDF_TARGET_ESP32S2
2021-02-03 02:14:17 -05:00
//On ESP32S2, each SPI controller has its own DMA channel
2021-02-19 22:03:28 -05:00
success = spicommon_dma_chan_claim ( host_id , & actual_dma_chan ) ;
2021-01-26 07:18:52 -05:00
# endif //#if CONFIG_IDF_TARGET_XXX
2021-02-19 22:03:28 -05:00
} else {
success = spicommon_dma_chan_claim ( ( int ) dma_chan , & actual_dma_chan ) ;
2021-01-26 07:18:52 -05:00
}
2021-02-19 22:03:28 -05:00
//On ESP32 and ESP32S2, actual_tx_dma_chan and actual_rx_dma_chan are always same
* out_actual_tx_dma_chan = actual_dma_chan ;
* out_actual_rx_dma_chan = actual_dma_chan ;
2021-02-03 02:14:17 -05:00
if ( ! success ) {
SPI_CHECK ( false , " no available dma channel " , ESP_ERR_NOT_FOUND ) ;
2021-01-26 07:18:52 -05:00
}
2021-02-03 02:14:17 -05:00
spicommon_connect_spi_and_dma ( host_id , * out_actual_tx_dma_chan ) ;
return ret ;
}
2021-01-26 07:18:52 -05:00
2021-01-27 08:56:16 -05:00
# else //SOC_GDMA_SUPPORTED
2021-02-19 22:03:28 -05:00
static 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 )
2021-02-03 02:14:17 -05:00
{
assert ( is_valid_host ( host_id ) ) ;
2021-02-19 22:03:28 -05:00
assert ( dma_chan = = SPI_DMA_CH_AUTO ) ;
2021-01-27 08:56:16 -05:00
2021-02-03 02:14:17 -05:00
esp_err_t ret = ESP_OK ;
2021-01-27 08:56:16 -05:00
spicommon_bus_context_t * ctx = bus_ctx [ host_id ] ;
2021-02-19 22:03:28 -05:00
if ( dma_chan = = SPI_DMA_CH_AUTO ) {
2021-01-27 08:56:16 -05:00
gdma_channel_alloc_config_t tx_alloc_config = {
. flags . reserve_sibling = 1 ,
. direction = GDMA_CHANNEL_DIRECTION_TX ,
} ;
ret = gdma_new_channel ( & tx_alloc_config , & ctx - > tx_channel ) ;
if ( ret ! = ESP_OK ) {
return ret ;
}
gdma_channel_alloc_config_t rx_alloc_config = {
. direction = GDMA_CHANNEL_DIRECTION_RX ,
. sibling_chan = ctx - > tx_channel ,
} ;
ret = gdma_new_channel ( & rx_alloc_config , & ctx - > rx_channel ) ;
if ( ret ! = ESP_OK ) {
return ret ;
}
2021-02-03 02:14:17 -05:00
if ( host_id = = SPI2_HOST ) {
2021-01-27 08:56:16 -05:00
gdma_connect ( ctx - > rx_channel , GDMA_MAKE_TRIGGER ( GDMA_TRIG_PERIPH_SPI , 2 ) ) ;
gdma_connect ( ctx - > tx_channel , GDMA_MAKE_TRIGGER ( GDMA_TRIG_PERIPH_SPI , 2 ) ) ;
}
# if (SOC_SPI_PERIPH_NUM >= 3)
2021-02-03 02:14:17 -05:00
else if ( host_id = = SPI3_HOST ) {
2021-01-27 08:56:16 -05:00
gdma_connect ( ctx - > rx_channel , GDMA_MAKE_TRIGGER ( GDMA_TRIG_PERIPH_SPI , 3 ) ) ;
gdma_connect ( ctx - > tx_channel , GDMA_MAKE_TRIGGER ( GDMA_TRIG_PERIPH_SPI , 3 ) ) ;
}
# endif
2021-02-03 02:14:17 -05:00
gdma_get_channel_id ( ctx - > tx_channel , ( int * ) out_actual_tx_dma_chan ) ;
gdma_get_channel_id ( ctx - > rx_channel , ( int * ) out_actual_rx_dma_chan ) ;
2021-01-27 08:56:16 -05:00
}
return ret ;
}
2021-02-03 02:14:17 -05:00
# endif //#if !SOC_GDMA_SUPPORTED
2021-01-27 08:56:16 -05:00
2021-02-19 22:03:28 -05:00
esp_err_t spicommon_slave_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 )
2021-01-27 08:56:16 -05:00
{
2021-02-03 02:14:17 -05:00
assert ( is_valid_host ( host_id ) ) ;
2021-02-19 22:03:28 -05:00
# if CONFIG_IDF_TARGET_ESP32
assert ( dma_chan > SPI_DMA_DISABLED & & dma_chan < = SPI_DMA_CH_AUTO ) ;
# elif CONFIG_IDF_TARGET_ESP32S2
assert ( dma_chan = = ( int ) host_id | | dma_chan = = SPI_DMA_CH_AUTO ) ;
# endif
2021-02-03 02:14:17 -05:00
2021-01-27 08:56:16 -05:00
esp_err_t ret = ESP_OK ;
2021-02-03 02:14:17 -05:00
uint32_t actual_tx_dma_chan = 0 ;
uint32_t actual_rx_dma_chan = 0 ;
2021-01-27 08:56:16 -05:00
spicommon_bus_context_t * ctx = ( spicommon_bus_context_t * ) calloc ( 1 , sizeof ( spicommon_bus_context_t ) ) ;
if ( ! ctx ) {
ret = ESP_ERR_NO_MEM ;
goto cleanup ;
}
bus_ctx [ host_id ] = ctx ;
ctx - > host_id = host_id ;
2021-02-03 02:14:17 -05:00
ret = spicommon_dma_chan_alloc ( host_id , dma_chan , & actual_tx_dma_chan , & actual_rx_dma_chan ) ;
2021-01-27 08:56:16 -05:00
if ( ret ! = ESP_OK ) {
goto cleanup ;
}
2021-02-03 02:14:17 -05:00
ctx - > bus_attr . tx_dma_chan = actual_tx_dma_chan ;
ctx - > bus_attr . rx_dma_chan = actual_rx_dma_chan ;
* out_actual_tx_dma_chan = actual_tx_dma_chan ;
* out_actual_rx_dma_chan = actual_rx_dma_chan ;
2021-01-27 08:56:16 -05:00
return ret ;
cleanup :
free ( ctx ) ;
ctx = NULL ;
return ret ;
}
//----------------------------------------------------------free dma periph-------------------------------------------------------//
2021-02-03 02:14:17 -05:00
static esp_err_t spicommon_dma_chan_free ( spi_host_device_t host_id )
2021-01-27 08:56:16 -05:00
{
2021-02-03 02:14:17 -05:00
assert ( is_valid_host ( host_id ) ) ;
spicommon_bus_context_t * ctx = bus_ctx [ host_id ] ;
2021-01-27 08:56:16 -05:00
# if !SOC_GDMA_SUPPORTED
2021-02-03 02:14:17 -05:00
//On ESP32S2, each SPI controller has its own DMA channel
int dma_chan = ctx - > bus_attr . tx_dma_chan ;
assert ( spi_dma_chan_enabled & BIT ( dma_chan ) ) ;
2021-01-27 08:56:16 -05:00
portENTER_CRITICAL ( & spi_dma_spinlock ) ;
2021-02-03 02:14:17 -05:00
spi_dma_chan_enabled & = ~ BIT ( dma_chan ) ;
2021-01-27 08:56:16 -05:00
periph_module_disable ( get_dma_periph ( dma_chan ) ) ;
portEXIT_CRITICAL ( & spi_dma_spinlock ) ;
# else //SOC_GDMA_SUPPORTED
if ( ctx - > rx_channel ) {
gdma_disconnect ( ctx - > rx_channel ) ;
gdma_del_channel ( ctx - > rx_channel ) ;
}
if ( ctx - > tx_channel ) {
gdma_disconnect ( ctx - > tx_channel ) ;
gdma_del_channel ( ctx - > tx_channel ) ;
}
2021-01-26 07:18:52 -05:00
# endif
return ESP_OK ;
}
2021-02-03 02:14:17 -05:00
esp_err_t spicommon_slave_free_dma ( spi_host_device_t host_id )
2021-01-27 08:56:16 -05:00
{
2021-02-03 02:14:17 -05:00
assert ( is_valid_host ( host_id ) ) ;
2021-01-27 08:56:16 -05:00
2021-02-03 02:14:17 -05:00
esp_err_t ret = spicommon_dma_chan_free ( host_id ) ;
2021-01-27 08:56:16 -05:00
free ( bus_ctx [ host_id ] ) ;
bus_ctx [ host_id ] = NULL ;
return ret ;
}
//----------------------------------------------------------IO general-------------------------------------------------------//
2019-02-11 01:17:31 -05:00
static bool bus_uses_iomux_pins ( spi_host_device_t host , const spi_bus_config_t * bus_config )
{
if ( bus_config - > sclk_io_num > = 0 & &
2020-11-26 03:56:13 -05:00
bus_config - > sclk_io_num ! = spi_periph_signal [ host ] . spiclk_iomux_pin ) {
return false ;
}
2019-02-11 01:17:31 -05:00
if ( bus_config - > quadwp_io_num > = 0 & &
2020-11-26 03:56:13 -05:00
bus_config - > quadwp_io_num ! = spi_periph_signal [ host ] . spiwp_iomux_pin ) {
return false ;
}
2019-02-11 01:17:31 -05:00
if ( bus_config - > quadhd_io_num > = 0 & &
2020-11-26 03:56:13 -05:00
bus_config - > quadhd_io_num ! = spi_periph_signal [ host ] . spihd_iomux_pin ) {
return false ;
}
2019-02-11 01:17:31 -05:00
if ( bus_config - > mosi_io_num > = 0 & &
2020-11-26 03:56:13 -05:00
bus_config - > mosi_io_num ! = spi_periph_signal [ host ] . spid_iomux_pin ) {
return false ;
}
2019-02-11 01:17:31 -05:00
if ( bus_config - > miso_io_num > = 0 & &
2020-11-26 03:56:13 -05:00
bus_config - > miso_io_num ! = spi_periph_signal [ host ] . spiq_iomux_pin ) {
return false ;
}
2019-02-11 01:17:31 -05:00
return true ;
}
2017-03-31 03:05:25 -04:00
/*
Do the common stuff to hook up a SPI host to a bus defined by a bunch of GPIO pins . Feed it a host number and a
2018-04-24 03:34:07 -04:00
bus config struct and it ' ll set up the GPIO matrix and enable the device . If a pin is set to non - negative value ,
2018-03-21 08:42:45 -04:00
it should be able to be initialized .
2017-03-31 03:05:25 -04:00
*/
2021-01-27 08:56:16 -05:00
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 )
2017-03-31 03:05:25 -04:00
{
2020-09-08 05:05:49 -04:00
uint32_t temp_flag = 0 ;
2018-03-21 08:42:45 -04:00
2019-02-11 01:17:31 -05:00
bool miso_need_output ;
bool mosi_need_output ;
bool sclk_need_output ;
if ( ( flags & SPICOMMON_BUSFLAG_MASTER ) ! = 0 ) {
//initial for master
miso_need_output = ( ( flags & SPICOMMON_BUSFLAG_DUAL ) ! = 0 ) ? true : false ;
mosi_need_output = true ;
sclk_need_output = true ;
} else {
//initial for slave
miso_need_output = true ;
mosi_need_output = ( ( flags & SPICOMMON_BUSFLAG_DUAL ) ! = 0 ) ? true : false ;
sclk_need_output = false ;
}
const bool wp_need_output = true ;
const bool hd_need_output = true ;
//check pin capabilities
2018-03-21 08:42:45 -04:00
if ( bus_config - > sclk_io_num > = 0 ) {
temp_flag | = SPICOMMON_BUSFLAG_SCLK ;
2019-02-11 01:17:31 -05:00
SPI_CHECK_PIN ( bus_config - > sclk_io_num , " sclk " , sclk_need_output ) ;
2017-03-31 03:05:25 -04:00
}
2018-03-21 08:42:45 -04:00
if ( bus_config - > quadwp_io_num > = 0 ) {
2019-02-11 01:17:31 -05:00
SPI_CHECK_PIN ( bus_config - > quadwp_io_num , " wp " , wp_need_output ) ;
2017-03-31 03:05:25 -04:00
}
2018-03-21 08:42:45 -04:00
if ( bus_config - > quadhd_io_num > = 0 ) {
2019-02-11 01:17:31 -05:00
SPI_CHECK_PIN ( bus_config - > quadhd_io_num , " hd " , hd_need_output ) ;
2017-12-27 21:31:23 -05:00
}
2019-02-11 01:17:31 -05:00
//set flags for QUAD mode according to the existence of wp and hd
if ( bus_config - > quadhd_io_num > = 0 & & bus_config - > quadwp_io_num > = 0 ) temp_flag | = SPICOMMON_BUSFLAG_WPHD ;
2018-03-21 08:42:45 -04:00
if ( bus_config - > mosi_io_num > = 0 ) {
temp_flag | = SPICOMMON_BUSFLAG_MOSI ;
2019-02-11 01:17:31 -05:00
SPI_CHECK_PIN ( bus_config - > mosi_io_num , " mosi " , mosi_need_output ) ;
2018-03-21 08:42:45 -04:00
}
if ( bus_config - > miso_io_num > = 0 ) {
temp_flag | = SPICOMMON_BUSFLAG_MISO ;
2019-02-11 01:17:31 -05:00
SPI_CHECK_PIN ( bus_config - > miso_io_num , " miso " , miso_need_output ) ;
2018-03-21 08:42:45 -04:00
}
2018-04-24 03:34:07 -04:00
//set flags for DUAL mode according to output-capability of MOSI and MISO pins.
if ( ( bus_config - > mosi_io_num < 0 | | GPIO_IS_VALID_OUTPUT_GPIO ( bus_config - > mosi_io_num ) ) & &
2018-03-21 08:42:45 -04:00
( bus_config - > miso_io_num < 0 | | GPIO_IS_VALID_OUTPUT_GPIO ( bus_config - > miso_io_num ) ) ) {
temp_flag | = SPICOMMON_BUSFLAG_DUAL ;
2018-04-24 03:34:07 -04:00
}
2019-02-11 01:17:31 -05:00
//check if the selected pins correspond to the iomux pins of the peripheral
2020-04-29 04:18:25 -04:00
bool use_iomux = ! ( flags & SPICOMMON_BUSFLAG_GPIO_PINS ) & & bus_uses_iomux_pins ( host , bus_config ) ;
if ( use_iomux ) {
temp_flag | = SPICOMMON_BUSFLAG_IOMUX_PINS ;
} else {
temp_flag | = SPICOMMON_BUSFLAG_GPIO_PINS ;
}
2019-02-11 01:17:31 -05:00
uint32_t missing_flag = flags & ~ temp_flag ;
missing_flag & = ~ SPICOMMON_BUSFLAG_MASTER ; //don't check this flag
if ( missing_flag ! = 0 ) {
//check pins existence
if ( missing_flag & SPICOMMON_BUSFLAG_SCLK ) ESP_LOGE ( SPI_TAG , " sclk pin required. " ) ;
if ( missing_flag & SPICOMMON_BUSFLAG_MOSI ) ESP_LOGE ( SPI_TAG , " mosi pin required. " ) ;
if ( missing_flag & SPICOMMON_BUSFLAG_MISO ) ESP_LOGE ( SPI_TAG , " miso pin required. " ) ;
if ( missing_flag & SPICOMMON_BUSFLAG_DUAL ) ESP_LOGE ( SPI_TAG , " not both mosi and miso output capable " ) ;
if ( missing_flag & SPICOMMON_BUSFLAG_WPHD ) ESP_LOGE ( SPI_TAG , " both wp and hd required. " ) ;
2019-06-13 02:12:54 -04:00
if ( missing_flag & SPICOMMON_BUSFLAG_IOMUX_PINS ) ESP_LOGE ( SPI_TAG , " not using iomux pins " ) ;
2019-02-11 01:17:31 -05:00
SPI_CHECK ( missing_flag = = 0 , " not all required capabilities satisfied. " , ESP_ERR_INVALID_ARG ) ;
}
2017-03-31 03:05:25 -04:00
2018-06-05 13:36:01 -04:00
if ( use_iomux ) {
//All SPI iomux pin selections resolve to 1, so we put that here instead of trying to figure
2017-03-31 03:05:25 -04:00
//out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway.
2018-10-05 03:39:32 -04:00
ESP_LOGD ( SPI_TAG , " SPI%d use iomux pins. " , host + 1 ) ;
2018-04-24 03:34:07 -04:00
if ( bus_config - > mosi_io_num > = 0 ) {
2018-06-08 03:32:43 -04:00
gpio_iomux_in ( bus_config - > mosi_io_num , spi_periph_signal [ host ] . spid_in ) ;
2019-06-05 22:57:29 -04:00
gpio_iomux_out ( bus_config - > mosi_io_num , spi_periph_signal [ host ] . func , false ) ;
2018-04-24 03:34:07 -04:00
}
if ( bus_config - > miso_io_num > = 0 ) {
2018-06-08 03:32:43 -04:00
gpio_iomux_in ( bus_config - > miso_io_num , spi_periph_signal [ host ] . spiq_in ) ;
2019-06-13 02:12:54 -04:00
gpio_iomux_out ( bus_config - > miso_io_num , spi_periph_signal [ host ] . func , false ) ;
2018-04-24 03:34:07 -04:00
}
if ( bus_config - > quadwp_io_num > = 0 ) {
2018-06-08 03:32:43 -04:00
gpio_iomux_in ( bus_config - > quadwp_io_num , spi_periph_signal [ host ] . spiwp_in ) ;
2019-06-05 22:57:29 -04:00
gpio_iomux_out ( bus_config - > quadwp_io_num , spi_periph_signal [ host ] . func , false ) ;
2018-04-24 03:34:07 -04:00
}
if ( bus_config - > quadhd_io_num > = 0 ) {
2018-06-08 03:32:43 -04:00
gpio_iomux_in ( bus_config - > quadhd_io_num , spi_periph_signal [ host ] . spihd_in ) ;
2019-06-05 22:57:29 -04:00
gpio_iomux_out ( bus_config - > quadhd_io_num , spi_periph_signal [ host ] . func , false ) ;
2018-04-24 03:34:07 -04:00
}
if ( bus_config - > sclk_io_num > = 0 ) {
2018-06-08 03:32:43 -04:00
gpio_iomux_in ( bus_config - > sclk_io_num , spi_periph_signal [ host ] . spiclk_in ) ;
2019-06-13 02:12:54 -04:00
gpio_iomux_out ( bus_config - > sclk_io_num , spi_periph_signal [ host ] . func , false ) ;
2018-04-24 03:34:07 -04:00
}
2019-06-13 02:12:54 -04:00
temp_flag | = SPICOMMON_BUSFLAG_IOMUX_PINS ;
2017-03-31 03:05:25 -04:00
} else {
2018-03-21 08:42:45 -04:00
//Use GPIO matrix
2018-10-05 03:39:32 -04:00
ESP_LOGD ( SPI_TAG , " SPI%d use gpio matrix. " , host + 1 ) ;
2017-12-12 04:00:02 -05:00
if ( bus_config - > mosi_io_num > = 0 ) {
2019-02-11 01:17:31 -05:00
if ( mosi_need_output | | ( temp_flag & SPICOMMON_BUSFLAG_DUAL ) ) {
2018-03-21 08:42:45 -04:00
gpio_set_direction ( bus_config - > mosi_io_num , GPIO_MODE_INPUT_OUTPUT ) ;
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_out_signal ( bus_config - > mosi_io_num , spi_periph_signal [ host ] . spid_out , false , false ) ;
2018-03-21 08:42:45 -04:00
} else {
gpio_set_direction ( bus_config - > mosi_io_num , GPIO_MODE_INPUT ) ;
}
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_in_signal ( bus_config - > mosi_io_num , spi_periph_signal [ host ] . spid_in , false ) ;
2020-01-16 22:47:08 -05:00
# if CONFIG_IDF_TARGET_ESP32S2
2019-06-05 22:57:29 -04:00
PIN_INPUT_ENABLE ( GPIO_PIN_MUX_REG [ bus_config - > mosi_io_num ] ) ;
# endif
2021-03-15 22:55:05 -04:00
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ bus_config - > mosi_io_num ] , FUNC_GPIO ) ;
2017-03-31 03:05:25 -04:00
}
2017-12-12 04:00:02 -05:00
if ( bus_config - > miso_io_num > = 0 ) {
2019-02-11 01:17:31 -05:00
if ( miso_need_output | | ( temp_flag & SPICOMMON_BUSFLAG_DUAL ) ) {
2018-03-21 08:42:45 -04:00
gpio_set_direction ( bus_config - > miso_io_num , GPIO_MODE_INPUT_OUTPUT ) ;
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_out_signal ( bus_config - > miso_io_num , spi_periph_signal [ host ] . spiq_out , false , false ) ;
2018-03-21 08:42:45 -04:00
} else {
gpio_set_direction ( bus_config - > miso_io_num , GPIO_MODE_INPUT ) ;
}
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_in_signal ( bus_config - > miso_io_num , spi_periph_signal [ host ] . spiq_in , false ) ;
2020-01-16 22:47:08 -05:00
# if CONFIG_IDF_TARGET_ESP32S2
2019-06-05 22:57:29 -04:00
PIN_INPUT_ENABLE ( GPIO_PIN_MUX_REG [ bus_config - > miso_io_num ] ) ;
# endif
2021-03-15 22:55:05 -04:00
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ bus_config - > miso_io_num ] , FUNC_GPIO ) ;
2017-03-31 03:05:25 -04:00
}
2018-03-21 08:42:45 -04:00
if ( bus_config - > quadwp_io_num > = 0 ) {
2017-04-24 04:10:37 -04:00
gpio_set_direction ( bus_config - > quadwp_io_num , GPIO_MODE_INPUT_OUTPUT ) ;
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_out_signal ( bus_config - > quadwp_io_num , spi_periph_signal [ host ] . spiwp_out , false , false ) ;
esp_rom_gpio_connect_in_signal ( bus_config - > quadwp_io_num , spi_periph_signal [ host ] . spiwp_in , false ) ;
2020-01-16 22:47:08 -05:00
# if CONFIG_IDF_TARGET_ESP32S2
2019-06-05 22:57:29 -04:00
PIN_INPUT_ENABLE ( GPIO_PIN_MUX_REG [ bus_config - > quadwp_io_num ] ) ;
# endif
2021-03-15 22:55:05 -04:00
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ bus_config - > quadwp_io_num ] , FUNC_GPIO ) ;
2017-03-31 03:05:25 -04:00
}
2018-03-21 08:42:45 -04:00
if ( bus_config - > quadhd_io_num > = 0 ) {
2017-04-24 04:10:37 -04:00
gpio_set_direction ( bus_config - > quadhd_io_num , GPIO_MODE_INPUT_OUTPUT ) ;
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_out_signal ( bus_config - > quadhd_io_num , spi_periph_signal [ host ] . spihd_out , false , false ) ;
esp_rom_gpio_connect_in_signal ( bus_config - > quadhd_io_num , spi_periph_signal [ host ] . spihd_in , false ) ;
2020-01-16 22:47:08 -05:00
# if CONFIG_IDF_TARGET_ESP32S2
2019-06-05 22:57:29 -04:00
PIN_INPUT_ENABLE ( GPIO_PIN_MUX_REG [ bus_config - > quadhd_io_num ] ) ;
# endif
2021-03-15 22:55:05 -04:00
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ bus_config - > quadhd_io_num ] , FUNC_GPIO ) ;
2017-03-31 03:05:25 -04:00
}
2017-12-12 04:00:02 -05:00
if ( bus_config - > sclk_io_num > = 0 ) {
2019-02-11 01:17:31 -05:00
if ( sclk_need_output ) {
gpio_set_direction ( bus_config - > sclk_io_num , GPIO_MODE_INPUT_OUTPUT ) ;
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_out_signal ( bus_config - > sclk_io_num , spi_periph_signal [ host ] . spiclk_out , false , false ) ;
2019-02-11 01:17:31 -05:00
} else {
gpio_set_direction ( bus_config - > sclk_io_num , GPIO_MODE_INPUT ) ;
}
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_in_signal ( bus_config - > sclk_io_num , spi_periph_signal [ host ] . spiclk_in , false ) ;
2020-01-16 22:47:08 -05:00
# if CONFIG_IDF_TARGET_ESP32S2
2019-06-05 22:57:29 -04:00
PIN_INPUT_ENABLE ( GPIO_PIN_MUX_REG [ bus_config - > sclk_io_num ] ) ;
# endif
2021-03-15 22:55:05 -04:00
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ bus_config - > sclk_io_num ] , FUNC_GPIO ) ;
2017-03-31 03:05:25 -04:00
}
}
2018-03-21 08:42:45 -04:00
if ( flags_o ) * flags_o = temp_flag ;
2017-03-31 03:05:25 -04:00
return ESP_OK ;
}
2018-06-08 03:33:04 -04:00
esp_err_t spicommon_bus_free_io_cfg ( const spi_bus_config_t * bus_cfg )
{
int pin_array [ ] = {
bus_cfg - > mosi_io_num ,
bus_cfg - > miso_io_num ,
bus_cfg - > sclk_io_num ,
bus_cfg - > quadwp_io_num ,
bus_cfg - > quadhd_io_num ,
} ;
for ( int i = 0 ; i < sizeof ( pin_array ) / sizeof ( int ) ; i + + ) {
const int io = pin_array [ i ] ;
if ( io > = 0 & & GPIO_IS_VALID_GPIO ( io ) ) gpio_reset_pin ( io ) ;
}
return ESP_OK ;
}
2017-04-26 23:24:44 -04:00
void spicommon_cs_initialize ( spi_host_device_t host , int cs_io_num , int cs_num , int force_gpio_matrix )
{
2018-06-08 03:32:43 -04:00
if ( ! force_gpio_matrix & & cs_io_num = = spi_periph_signal [ host ] . spics0_iomux_pin & & cs_num = = 0 ) {
2017-03-31 03:05:25 -04:00
//The cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define.
2018-06-08 03:32:43 -04:00
gpio_iomux_in ( cs_io_num , spi_periph_signal [ host ] . spics_in ) ;
2019-06-13 02:12:54 -04:00
gpio_iomux_out ( cs_io_num , spi_periph_signal [ host ] . func , false ) ;
2017-03-31 03:05:25 -04:00
} else {
//Use GPIO matrix
2019-02-11 01:17:31 -05:00
if ( GPIO_IS_VALID_OUTPUT_GPIO ( cs_io_num ) ) {
gpio_set_direction ( cs_io_num , GPIO_MODE_INPUT_OUTPUT ) ;
2020-06-19 00:00:58 -04:00
esp_rom_gpio_connect_out_signal ( cs_io_num , spi_periph_signal [ host ] . spics_out [ cs_num ] , false , false ) ;
2019-02-11 01:17:31 -05:00
} else {
gpio_set_direction ( cs_io_num , GPIO_MODE_INPUT ) ;
}
2020-06-19 00:00:58 -04:00
if ( cs_num = = 0 ) esp_rom_gpio_connect_in_signal ( cs_io_num , spi_periph_signal [ host ] . spics_in , false ) ;
2019-06-05 22:57:29 -04:00
PIN_INPUT_ENABLE ( GPIO_PIN_MUX_REG [ cs_io_num ] ) ;
2021-03-15 22:55:05 -04:00
gpio_hal_iomux_func_sel ( GPIO_PIN_MUX_REG [ cs_io_num ] , FUNC_GPIO ) ;
2017-03-31 03:05:25 -04:00
}
}
2018-06-08 03:33:04 -04:00
void spicommon_cs_free_io ( int cs_gpio_num )
{
assert ( cs_gpio_num > = 0 & & GPIO_IS_VALID_GPIO ( cs_gpio_num ) ) ;
gpio_reset_pin ( cs_gpio_num ) ;
}
2019-06-25 23:42:02 -04:00
bool spicommon_bus_using_iomux ( spi_host_device_t host )
2017-04-26 23:24:44 -04:00
{
2019-06-25 23:42:02 -04:00
# 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
2017-03-31 03:05:25 -04:00
2019-06-25 23:42:02 -04:00
CHECK_IOMUX_PIN ( host , spid ) ;
CHECK_IOMUX_PIN ( host , spiq ) ;
CHECK_IOMUX_PIN ( host , spiwp ) ;
CHECK_IOMUX_PIN ( host , spihd ) ;
return true ;
}
2017-03-31 03:05:25 -04:00
2019-10-24 07:00:26 -04:00
void spi_bus_main_set_lock ( spi_bus_lock_handle_t lock )
{
bus_ctx [ 0 ] - > bus_attr . lock = lock ;
}
spi_bus_lock_handle_t spi_bus_lock_get_by_id ( spi_host_device_t host_id )
{
return bus_ctx [ host_id ] - > bus_attr . lock ;
}
2021-01-27 08:56:16 -05:00
//----------------------------------------------------------master bus init-------------------------------------------------------//
2021-02-19 22:03:28 -05:00
esp_err_t spi_bus_initialize ( spi_host_device_t host_id , const spi_bus_config_t * bus_config , spi_dma_chan_t dma_chan )
2019-10-24 07:00:26 -04:00
{
esp_err_t err = ESP_OK ;
spicommon_bus_context_t * ctx = NULL ;
spi_bus_attr_t * bus_attr = NULL ;
2021-01-27 08:56:16 -05:00
uint32_t actual_tx_dma_chan = 0 ;
uint32_t actual_rx_dma_chan = 0 ;
2021-01-26 07:18:52 -05:00
2019-10-24 07:00:26 -04:00
SPI_CHECK ( is_valid_host ( host_id ) , " invalid host_id " , ESP_ERR_INVALID_ARG ) ;
SPI_CHECK ( bus_ctx [ host_id ] = = NULL , " SPI bus already initialized. " , ESP_ERR_INVALID_STATE ) ;
# ifdef CONFIG_IDF_TARGET_ESP32
2021-02-19 22:03:28 -05:00
SPI_CHECK ( dma_chan > = SPI_DMA_DISABLED & & dma_chan < = SPI_DMA_CH_AUTO , " invalid dma channel " , ESP_ERR_INVALID_ARG ) ;
2019-10-24 07:00:26 -04:00
# elif CONFIG_IDF_TARGET_ESP32S2
2021-02-19 22:03:28 -05:00
SPI_CHECK ( dma_chan = = SPI_DMA_DISABLED | | dma_chan = = ( int ) host_id | | dma_chan = = SPI_DMA_CH_AUTO , " invalid dma channel " , ESP_ERR_INVALID_ARG ) ;
2021-01-26 07:18:52 -05:00
# elif SOC_GDMA_SUPPORTED
2021-02-19 22:03:28 -05:00
SPI_CHECK ( dma_chan = = SPI_DMA_DISABLED | | dma_chan = = SPI_DMA_CH_AUTO , " invalid dma channel, chip only support spi dma channel auto-alloc " , ESP_ERR_INVALID_ARG ) ;
2019-10-24 07:00:26 -04:00
# endif
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_MASTER_ISR_IN_IRAM
SPI_CHECK ( ( bus_config - > intr_flags & ESP_INTR_FLAG_IRAM ) = = 0 , " ESP_INTR_FLAG_IRAM should be disabled when CONFIG_SPI_MASTER_ISR_IN_IRAM is not set. " , ESP_ERR_INVALID_ARG ) ;
# endif
bool spi_chan_claimed = spicommon_periph_claim ( host_id , " spi master " ) ;
SPI_CHECK ( spi_chan_claimed , " host_id already in use " , ESP_ERR_INVALID_STATE ) ;
//clean and initialize the context
2021-01-26 07:18:52 -05:00
ctx = ( spicommon_bus_context_t * ) calloc ( 1 , sizeof ( spicommon_bus_context_t ) ) ;
2019-10-24 07:00:26 -04:00
if ( ! ctx ) {
err = ESP_ERR_NO_MEM ;
goto cleanup ;
}
2021-01-27 08:56:16 -05:00
bus_ctx [ host_id ] = ctx ;
2021-01-26 07:18:52 -05:00
ctx - > host_id = host_id ;
2019-10-24 07:00:26 -04:00
bus_attr = & ctx - > bus_attr ;
2021-01-26 07:18:52 -05:00
bus_attr - > bus_cfg = * bus_config ;
2021-02-19 22:03:28 -05:00
if ( dma_chan ! = SPI_DMA_DISABLED ) {
2021-01-27 08:56:16 -05:00
bus_attr - > dma_enabled = 1 ;
2021-02-03 02:14:17 -05:00
err = spicommon_dma_chan_alloc ( host_id , dma_chan , & actual_tx_dma_chan , & actual_rx_dma_chan ) ;
2021-01-26 07:18:52 -05:00
if ( err ! = ESP_OK ) {
2021-01-27 08:56:16 -05:00
goto cleanup ;
2021-01-26 07:18:52 -05:00
}
2021-01-27 08:56:16 -05:00
bus_attr - > tx_dma_chan = actual_tx_dma_chan ;
bus_attr - > rx_dma_chan = actual_rx_dma_chan ;
2021-01-26 07:18:52 -05:00
2019-10-24 07:00:26 -04:00
int dma_desc_ct = lldesc_get_required_num ( bus_config - > max_transfer_sz ) ;
if ( dma_desc_ct = = 0 ) dma_desc_ct = 1 ; //default to 4k when max is not given
bus_attr - > max_transfer_sz = dma_desc_ct * LLDESC_MAX_NUM_PER_DESC ;
bus_attr - > dmadesc_tx = heap_caps_malloc ( sizeof ( lldesc_t ) * dma_desc_ct , MALLOC_CAP_DMA ) ;
bus_attr - > dmadesc_rx = heap_caps_malloc ( sizeof ( lldesc_t ) * dma_desc_ct , MALLOC_CAP_DMA ) ;
if ( bus_attr - > dmadesc_tx = = NULL | | bus_attr - > dmadesc_rx = = NULL ) {
err = ESP_ERR_NO_MEM ;
goto cleanup ;
}
bus_attr - > dma_desc_num = dma_desc_ct ;
2021-01-26 07:18:52 -05:00
} else {
2021-01-27 08:56:16 -05:00
bus_attr - > dma_enabled = 0 ;
2021-01-26 07:18:52 -05:00
bus_attr - > max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE ;
bus_attr - > dma_desc_num = 0 ;
2019-10-24 07:00:26 -04:00
}
spi_bus_lock_config_t lock_config = {
. host_id = host_id ,
. cs_num = SOC_SPI_PERIPH_CS_NUM ( host_id ) ,
} ;
err = spi_bus_init_lock ( & bus_attr - > lock , & lock_config ) ;
if ( err ! = ESP_OK ) {
goto cleanup ;
}
# ifdef CONFIG_PM_ENABLE
err = esp_pm_lock_create ( ESP_PM_APB_FREQ_MAX , 0 , " spi_master " ,
& bus_attr - > pm_lock ) ;
if ( err ! = ESP_OK ) {
goto cleanup ;
}
# endif //CONFIG_PM_ENABLE
2021-01-27 08:56:16 -05:00
err = spicommon_bus_initialize_io ( host_id , bus_config , SPICOMMON_BUSFLAG_MASTER | bus_config - > flags , & bus_attr - > flags ) ;
2019-10-24 07:00:26 -04:00
if ( err ! = ESP_OK ) {
goto cleanup ;
}
return ESP_OK ;
cleanup :
if ( bus_attr ) {
# ifdef CONFIG_PM_ENABLE
esp_pm_lock_delete ( bus_attr - > pm_lock ) ;
# endif
if ( bus_attr - > lock ) {
spi_bus_deinit_lock ( bus_attr - > lock ) ;
}
free ( bus_attr - > dmadesc_tx ) ;
free ( bus_attr - > dmadesc_rx ) ;
2021-02-03 02:14:17 -05:00
bus_attr - > dmadesc_tx = NULL ;
bus_attr - > dmadesc_rx = NULL ;
2021-01-27 08:56:16 -05:00
if ( bus_attr - > dma_enabled ) {
2021-02-03 02:14:17 -05:00
spicommon_dma_chan_free ( host_id ) ;
2021-01-27 08:56:16 -05:00
}
2019-10-24 07:00:26 -04:00
}
spicommon_periph_free ( host_id ) ;
2021-01-27 08:56:16 -05:00
free ( bus_ctx [ host_id ] ) ;
bus_ctx [ host_id ] = NULL ;
2019-10-24 07:00:26 -04:00
return err ;
}
const spi_bus_attr_t * spi_bus_get_attr ( spi_host_device_t host_id )
{
if ( bus_ctx [ host_id ] = = NULL ) return NULL ;
return & bus_ctx [ host_id ] - > bus_attr ;
}
esp_err_t spi_bus_free ( spi_host_device_t host_id )
{
esp_err_t err = ESP_OK ;
spicommon_bus_context_t * ctx = bus_ctx [ host_id ] ;
spi_bus_attr_t * bus_attr = & ctx - > bus_attr ;
if ( ctx - > destroy_func ) {
err = ctx - > destroy_func ( ctx - > destroy_arg ) ;
}
spicommon_bus_free_io_cfg ( & bus_attr - > bus_cfg ) ;
# ifdef CONFIG_PM_ENABLE
esp_pm_lock_delete ( bus_attr - > pm_lock ) ;
# endif
spi_bus_deinit_lock ( bus_attr - > lock ) ;
free ( bus_attr - > dmadesc_rx ) ;
free ( bus_attr - > dmadesc_tx ) ;
2021-02-03 02:14:17 -05:00
bus_attr - > dmadesc_tx = NULL ;
bus_attr - > dmadesc_rx = NULL ;
2021-01-27 08:56:16 -05:00
if ( bus_attr - > dma_enabled > 0 ) {
2021-02-03 02:14:17 -05:00
spicommon_dma_chan_free ( host_id ) ;
2019-10-24 07:00:26 -04:00
}
spicommon_periph_free ( host_id ) ;
free ( ctx ) ;
bus_ctx [ host_id ] = NULL ;
return err ;
}
esp_err_t spi_bus_register_destroy_func ( spi_host_device_t host_id ,
spi_destroy_func_t f , void * arg )
{
bus_ctx [ host_id ] - > destroy_func = f ;
bus_ctx [ host_id ] - > destroy_arg = arg ;
return ESP_OK ;
}
2017-03-31 03:05:25 -04:00
/*
Code for workaround for DMA issue in ESP32 v0 / v1 silicon
*/
2019-06-13 02:19:31 -04:00
# if CONFIG_IDF_TARGET_ESP32
2017-04-26 23:24:44 -04:00
static volatile int dmaworkaround_channels_busy [ 2 ] = { 0 , 0 } ;
2017-03-31 03:05:25 -04:00
static dmaworkaround_cb_t dmaworkaround_cb ;
static void * dmaworkaround_cb_arg ;
2017-04-26 23:24:44 -04:00
static portMUX_TYPE dmaworkaround_mux = portMUX_INITIALIZER_UNLOCKED ;
static int dmaworkaround_waiting_for_chan = 0 ;
2019-06-13 02:19:31 -04:00
# endif
2017-03-31 03:05:25 -04:00
2017-04-26 23:24:44 -04:00
bool IRAM_ATTR spicommon_dmaworkaround_req_reset ( int dmachan , dmaworkaround_cb_t cb , void * arg )
2017-03-31 03:05:25 -04:00
{
2019-06-05 22:57:29 -04:00
# if CONFIG_IDF_TARGET_ESP32
2017-04-26 23:24:44 -04:00
int otherchan = ( dmachan = = 1 ) ? 2 : 1 ;
2017-03-31 03:05:25 -04:00
bool ret ;
2018-03-23 09:58:03 -04:00
portENTER_CRITICAL_ISR ( & dmaworkaround_mux ) ;
2017-05-08 04:11:46 -04:00
if ( dmaworkaround_channels_busy [ otherchan - 1 ] ) {
2017-03-31 03:05:25 -04:00
//Other channel is busy. Call back when it's done.
2017-04-26 23:24:44 -04:00
dmaworkaround_cb = cb ;
dmaworkaround_cb_arg = arg ;
dmaworkaround_waiting_for_chan = otherchan ;
ret = false ;
2017-03-31 03:05:25 -04:00
} else {
//Reset DMA
2017-10-02 02:48:16 -04:00
periph_module_reset ( PERIPH_SPI_DMA_MODULE ) ;
2017-04-26 23:24:44 -04:00
ret = true ;
2017-03-31 03:05:25 -04:00
}
2018-03-23 09:58:03 -04:00
portEXIT_CRITICAL_ISR ( & dmaworkaround_mux ) ;
2017-03-31 03:05:25 -04:00
return ret ;
2019-06-13 02:19:31 -04:00
# else
//no need to reset
2019-06-05 22:57:29 -04:00
return true ;
# endif
2017-03-31 03:05:25 -04:00
}
2019-07-16 05:33:30 -04:00
bool IRAM_ATTR spicommon_dmaworkaround_reset_in_progress ( void )
2017-03-31 03:05:25 -04:00
{
2019-06-13 02:19:31 -04:00
# if CONFIG_IDF_TARGET_ESP32
2017-04-26 23:24:44 -04:00
return ( dmaworkaround_waiting_for_chan ! = 0 ) ;
2019-06-13 02:19:31 -04:00
# else
return false ;
# endif
2017-03-31 03:05:25 -04:00
}
2017-04-26 23:24:44 -04:00
void IRAM_ATTR spicommon_dmaworkaround_idle ( int dmachan )
{
2019-06-05 22:57:29 -04:00
# if CONFIG_IDF_TARGET_ESP32
2018-03-23 09:58:03 -04:00
portENTER_CRITICAL_ISR ( & dmaworkaround_mux ) ;
2017-05-08 04:11:46 -04:00
dmaworkaround_channels_busy [ dmachan - 1 ] = 0 ;
2017-03-31 03:05:25 -04:00
if ( dmaworkaround_waiting_for_chan = = dmachan ) {
//Reset DMA
2017-10-02 02:48:16 -04:00
periph_module_reset ( PERIPH_SPI_DMA_MODULE ) ;
2017-04-26 23:24:44 -04:00
dmaworkaround_waiting_for_chan = 0 ;
2017-03-31 03:05:25 -04:00
//Call callback
dmaworkaround_cb ( dmaworkaround_cb_arg ) ;
}
2018-03-23 09:58:03 -04:00
portEXIT_CRITICAL_ISR ( & dmaworkaround_mux ) ;
2019-06-05 22:57:29 -04:00
# endif
2017-03-31 03:05:25 -04:00
}
2017-04-26 23:24:44 -04:00
void IRAM_ATTR spicommon_dmaworkaround_transfer_active ( int dmachan )
{
2019-06-13 02:19:31 -04:00
# if CONFIG_IDF_TARGET_ESP32
2018-03-23 09:58:03 -04:00
portENTER_CRITICAL_ISR ( & dmaworkaround_mux ) ;
2017-05-08 04:11:46 -04:00
dmaworkaround_channels_busy [ dmachan - 1 ] = 1 ;
2018-03-23 09:58:03 -04:00
portEXIT_CRITICAL_ISR ( & dmaworkaround_mux ) ;
2019-06-05 22:57:29 -04:00
# endif
2017-03-31 03:05:25 -04:00
}