2019-09-28 10:14:24 +00:00
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
# include "essl_sdio.h"
# include "esp_log.h"
# include "freertos/task.h"
# include "essl_internal.h"
2020-07-31 10:26:07 +00:00
# include "soc/soc_caps.h"
# if SOC_SDIO_SLAVE_SUPPORTED
# include "soc/host_reg.h"
2019-09-28 10:14:24 +00:00
static const char TAG [ ] = " essl_sdio " ;
2020-07-31 10:26:07 +00:00
# define HOST_SLCHOST_CONF_W_REG(pos) (HOST_SLCHOST_CONF_W0_REG+pos+(pos>23?4:0)+(pos>31?12:0))
2019-09-28 10:14:24 +00:00
# define ESSL_CMD53_END_ADDR 0x1f800
# define TX_BUFFER_MAX 0x1000
# define TX_BUFFER_MASK 0xFFF
# define RX_BYTE_MAX 0x100000
# define RX_BYTE_MASK 0xFFFFF
# define FUNC1_EN_MASK (BIT(1))
/**
* Initialize ` ` void ` ` over SDIO by this macro .
*/
# define ESSL_SDIO_DEFAULT_CONTEXT() (essl_dev_t){\
. init = essl_sdio_init , \
. wait_for_ready = essl_sdio_wait_for_ready , \
. get_tx_buffer_num = essl_sdio_get_tx_buffer_num , \
. update_tx_buffer_num = essl_sdio_update_tx_buffer_num , \
. get_rx_data_size = essl_sdio_get_rx_data_size , \
. update_rx_data_size = essl_sdio_update_rx_data_size , \
. send_packet = essl_sdio_send_packet , \
. get_packet = essl_sdio_get_packet , \
. write_reg = essl_sdio_write_reg , \
. read_reg = essl_sdio_read_reg , \
. wait_int = essl_sdio_wait_int , \
. send_slave_intr = essl_sdio_send_slave_intr , \
. get_intr = essl_sdio_get_intr , \
. clear_intr = essl_sdio_clear_intr , \
. set_intr_ena = essl_sdio_set_intr_ena , \
. reset_cnt = essl_sdio_reset_cnt , \
}
typedef struct {
//common part
uint16_t buffer_size ;
///< All data that do not fully fill a buffer is still counted as one buffer. E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer.
///< Buffer size of the slave pre-defined between host and slave before communication.
size_t tx_sent_buffers ; ///< Counter holding the amount of buffers already sent to ESP32 slave. Should be set to 0 when initialization.
size_t tx_sent_buffers_latest ; ///< The latest reading (from the slave) of counter holding the amount of buffers loaded. Should be set to 0 when initialization.
size_t rx_got_bytes ; ///< Counter holding the amount of bytes already received from ESP32 slave. Should be set to 0 when initialization.
size_t rx_got_bytes_latest ; ///< The latest reading (from the slave) of counter holding the amount of bytes to send. Should be set to 0 when initialization.
sdmmc_card_t * card ; ///< Initialized sdmmc_cmd card
uint16_t block_size ;
///< If this is too large, it takes time to send stuff bits; while if too small, intervals between blocks cost much.
///< Should be set according to length of data, and larger than ``TRANS_LEN_MAX/511``.
///< Block size of the SDIO function 1. After the initialization this will hold the value the slave really do. Valid value is 1-2048.
} essl_sdio_context_t ;
esp_err_t essl_sdio_update_tx_buffer_num ( void * arg , uint32_t wait_ms ) ;
esp_err_t essl_sdio_update_rx_data_size ( void * arg , uint32_t wait_ms ) ;
static inline esp_err_t essl_sdio_write_byte ( sdmmc_card_t * card , uint32_t addr , uint8_t val , uint8_t * val_o )
{
return sdmmc_io_write_byte ( card , 1 , addr & 0x3FF , val , val_o ) ;
}
static inline esp_err_t essl_sdio_write_bytes ( sdmmc_card_t * card , uint32_t addr , uint8_t * val , int len )
{
return sdmmc_io_write_bytes ( card , 1 , addr & 0x3FF , val , len ) ;
}
static inline esp_err_t essl_sdio_read_byte ( sdmmc_card_t * card , uint32_t addr , uint8_t * val_o )
{
return sdmmc_io_read_byte ( card , 1 , addr & 0x3FF , val_o ) ;
}
static inline esp_err_t essl_sdio_read_bytes ( sdmmc_card_t * card , uint32_t addr , uint8_t * val_o , int len )
{
return sdmmc_io_read_bytes ( card , 1 , addr & 0x3FF , val_o , len ) ;
}
esp_err_t essl_sdio_init_dev ( essl_handle_t * out_handle , const essl_sdio_config_t * config )
{
esp_err_t ret = ESP_OK ;
essl_sdio_context_t * arg = NULL ;
essl_dev_t * dev = NULL ;
arg = ( essl_sdio_context_t * ) heap_caps_malloc ( sizeof ( essl_sdio_context_t ) , MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT ) ;
dev = ( essl_dev_t * ) heap_caps_malloc ( sizeof ( essl_dev_t ) , MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT ) ;
if ( arg = = NULL | | dev = = NULL ) {
ret = ESP_ERR_NO_MEM ;
goto cleanup ;
}
* dev = ESSL_SDIO_DEFAULT_CONTEXT ( ) ;
dev - > args = arg ;
* arg = ( essl_sdio_context_t ) {
. card = config - > card ,
. block_size = 0x200 ,
. buffer_size = config - > recv_buffer_size ,
. tx_sent_buffers = 0 ,
. rx_got_bytes = 0 ,
} ;
* out_handle = dev ;
return ESP_OK ;
cleanup :
free ( arg ) ;
free ( dev ) ;
return ret ;
}
esp_err_t essl_sdio_deinit_dev ( essl_handle_t handle )
{
if ( handle ) free ( handle - > args ) ;
free ( handle ) ;
return ESP_OK ;
}
esp_err_t essl_sdio_init ( void * arg , uint32_t wait_ms )
{
essl_sdio_context_t * ctx = arg ;
esp_err_t err ;
uint8_t ioe ;
sdmmc_card_t * card = ctx - > card ;
err = sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_FN_ENABLE , & ioe ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " IOE: 0x%02x " , ioe ) ;
uint8_t ior = 0 ;
err = sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_FN_READY , & ior ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " IOR: 0x%02x " , ior ) ;
// enable function 1
ioe | = FUNC1_EN_MASK ;
err = sdmmc_io_write_byte ( card , 0 , SD_IO_CCCR_FN_ENABLE , ioe , & ioe ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " IOE: 0x%02x " , ioe ) ;
// wait for the card to become ready
while ( ( ior & FUNC1_EN_MASK ) = = 0 ) {
err = sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_FN_READY , & ior ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " IOR: 0x%02x " , ior ) ;
}
// get interrupt status
uint8_t ie ;
err = sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_INT_ENABLE , & ie ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " IE: 0x%02x " , ie ) ;
// enable interrupts for function 1&2 and master enable
ie | = BIT ( 0 ) | FUNC1_EN_MASK ;
err = sdmmc_io_write_byte ( card , 0 , SD_IO_CCCR_INT_ENABLE , ie , & ie ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " IE: 0x%02x " , ie ) ;
// get bus width register
uint8_t bus_width ;
err = sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_BUS_WIDTH , & bus_width ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " BUS_WIDTH: 0x%02x " , bus_width ) ;
// enable continuous SPI interrupts
bus_width | = CCCR_BUS_WIDTH_ECSI ;
err = sdmmc_io_write_byte ( card , 0 , SD_IO_CCCR_BUS_WIDTH , bus_width , & bus_width ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " BUS_WIDTH: 0x%02x " , bus_width ) ;
uint16_t bs = 512 ;
const uint8_t * bs_u8 = ( const uint8_t * ) & bs ;
uint16_t bs_read = 0 ;
uint8_t * bs_read_u8 = ( uint8_t * ) & bs_read ;
// Set block sizes for functions 0 to 512 bytes
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_BLKSIZEL , & bs_read_u8 [ 0 ] ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_BLKSIZEH , & bs_read_u8 [ 1 ] ) ) ;
ESP_LOGD ( TAG , " Function 0 BS: %04x " , ( int ) bs_read ) ;
ESP_ERROR_CHECK ( sdmmc_io_write_byte ( card , 0 , SD_IO_CCCR_BLKSIZEL , bs_u8 [ 0 ] , NULL ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_write_byte ( card , 0 , SD_IO_CCCR_BLKSIZEH , bs_u8 [ 1 ] , NULL ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_BLKSIZEL , & bs_read_u8 [ 0 ] ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_BLKSIZEH , & bs_read_u8 [ 1 ] ) ) ;
ESP_LOGD ( TAG , " Function 0 BS: %04x " , ( int ) bs_read ) ;
// Set block sizes for functions 1 to given value (default value = 512).
if ( ctx - > block_size > 0 | | ctx - > block_size < = 2048 ) {
bs = ctx - > block_size ;
} else {
bs = 512 ;
}
size_t offset = SD_IO_FBR_START * 1 ;
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , offset + SD_IO_CCCR_BLKSIZEL , & bs_read_u8 [ 0 ] ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , offset + SD_IO_CCCR_BLKSIZEH , & bs_read_u8 [ 1 ] ) ) ;
ESP_LOGD ( TAG , " Function 1 BS: %04x " , ( int ) bs_read ) ;
ESP_ERROR_CHECK ( sdmmc_io_write_byte ( card , 0 , offset + SD_IO_CCCR_BLKSIZEL , bs_u8 [ 0 ] , NULL ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_write_byte ( card , 0 , offset + SD_IO_CCCR_BLKSIZEH , bs_u8 [ 1 ] , NULL ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , offset + SD_IO_CCCR_BLKSIZEL , & bs_read_u8 [ 0 ] ) ) ;
ESP_ERROR_CHECK ( sdmmc_io_read_byte ( card , 0 , offset + SD_IO_CCCR_BLKSIZEH , & bs_read_u8 [ 1 ] ) ) ;
ESP_LOGD ( TAG , " Function 1 BS: %04x " , ( int ) bs_read ) ;
if ( bs_read ! = ctx - > block_size ) {
ESP_LOGW ( TAG , " Function1 block size %d different than set value %d " , bs_read , ctx - > block_size ) ;
ctx - > block_size = bs_read ;
}
return ESP_OK ;
}
esp_err_t essl_sdio_wait_for_ready ( void * arg , uint32_t wait_ms )
{
ESP_LOGV ( TAG , " wait_for_ioready " ) ;
esp_err_t err ;
sdmmc_card_t * card = ( ( essl_sdio_context_t * ) arg ) - > card ;
// wait for the card to become ready
uint8_t ior = 0 ;
while ( ( ior & FUNC1_EN_MASK ) = = 0 ) {
err = sdmmc_io_read_byte ( card , 0 , SD_IO_CCCR_FN_READY , & ior ) ;
if ( err ! = ESP_OK ) return err ;
ESP_LOGD ( TAG , " IOR: 0x%02x " , ior ) ;
}
return ESP_OK ;
}
esp_err_t essl_sdio_send_packet ( void * arg , const void * start , size_t length , uint32_t wait_ms )
{
essl_sdio_context_t * ctx = arg ;
uint16_t buffer_size = ctx - > buffer_size ;
int buffer_used = ( length + buffer_size - 1 ) / buffer_size ;
esp_err_t err ;
if ( essl_sdio_get_tx_buffer_num ( arg ) < buffer_used ) {
//slave has no enough buffer, try update for once
esp_err_t err = essl_sdio_update_tx_buffer_num ( arg , wait_ms ) ;
if ( err ! = ESP_OK ) {
return err ;
}
if ( essl_sdio_get_tx_buffer_num ( arg ) < buffer_used ) {
ESP_LOGV ( TAG , " buffer is not enough: %d, %d required. " , ctx - > tx_sent_buffers_latest , ctx - > tx_sent_buffers + buffer_used ) ;
return ESP_ERR_NOT_FOUND ;
}
}
ESP_LOGV ( TAG , " send_packet: len: %d " , length ) ;
uint8_t * start_ptr = ( uint8_t * ) start ;
uint32_t len_remain = length ;
do {
const int block_size = 512 ;
/* Though the driver supports to split packet of unaligned size into
* length of 4 x and 1 ~ 3 , we still send aligned size of data to get
* higher effeciency . The length is determined by the SDIO address , and
* the remainning will be discard by the slave hardware .
*/
int block_n = len_remain / block_size ;
int len_to_send ;
if ( block_n ) {
len_to_send = block_n * block_size ;
err = sdmmc_io_write_blocks ( ctx - > card , 1 , ESSL_CMD53_END_ADDR - len_remain , start_ptr , len_to_send ) ;
} else {
len_to_send = len_remain ;
err = sdmmc_io_write_bytes ( ctx - > card , 1 , ESSL_CMD53_END_ADDR - len_remain , start_ptr , ( len_to_send + 3 ) & ( ~ 3 ) ) ;
}
if ( err ! = ESP_OK ) return err ;
start_ptr + = len_to_send ;
len_remain - = len_to_send ;
} while ( len_remain ) ;
ctx - > tx_sent_buffers + = buffer_used ;
return ESP_OK ;
}
esp_err_t essl_sdio_get_packet ( void * arg , void * out_data , size_t size , uint32_t wait_ms )
{
essl_sdio_context_t * ctx = arg ;
esp_err_t err ;
ESP_LOGV ( TAG , " get_packet: read size=%d " , size ) ;
if ( essl_sdio_get_rx_data_size ( arg ) < size ) {
err = essl_sdio_update_rx_data_size ( arg , wait_ms ) ;
if ( err ! = ESP_OK ) {
return err ;
}
if ( essl_sdio_get_rx_data_size ( arg ) < size ) {
return ESP_ERR_NOT_FOUND ;
}
}
uint8_t * start = out_data ;
uint32_t len_remain = size ;
do {
const int block_size = 512 ; //currently our driver don't support block size other than 512
int len_to_send ;
int block_n = len_remain / block_size ;
if ( block_n ! = 0 ) {
len_to_send = block_n * block_size ;
err = sdmmc_io_read_blocks ( ctx - > card , 1 , ESSL_CMD53_END_ADDR - len_remain , start , len_to_send ) ;
} else {
len_to_send = len_remain ;
/* though the driver supports to split packet of unaligned size into length
* of 4 x and 1 ~ 3 , we still get aligned size of data to get higher
* effeciency . The length is determined by the SDIO address , and the
* remainning will be ignored by the slave hardware .
*/
err = sdmmc_io_read_bytes ( ctx - > card , 1 , ESSL_CMD53_END_ADDR - len_remain , start , ( len_to_send + 3 ) & ( ~ 3 ) ) ;
}
if ( err ! = ESP_OK ) return err ;
start + = len_to_send ;
len_remain - = len_to_send ;
ctx - > rx_got_bytes + = len_to_send ;
} while ( len_remain ! = 0 ) ;
return err ;
}
uint32_t essl_sdio_get_tx_buffer_num ( void * arg )
{
essl_sdio_context_t * ctx = arg ;
ESP_LOGV ( TAG , " tx latest: %d, sent: %d " , ctx - > tx_sent_buffers_latest , ctx - > tx_sent_buffers ) ;
return ( ctx - > tx_sent_buffers_latest + TX_BUFFER_MAX - ctx - > tx_sent_buffers ) % TX_BUFFER_MAX ;
}
esp_err_t essl_sdio_update_tx_buffer_num ( void * arg , uint32_t wait_ms )
{
essl_sdio_context_t * ctx = arg ;
uint32_t len ;
esp_err_t err ;
err = essl_sdio_read_bytes ( ctx - > card , HOST_SLC0HOST_TOKEN_RDATA_REG , ( uint8_t * ) & len , 4 ) ;
if ( err ! = ESP_OK ) return err ;
len = ( len > > 16 ) & TX_BUFFER_MASK ;
ctx - > tx_sent_buffers_latest = len ;
ESP_LOGV ( TAG , " update_tx_buffer_num: %d " , len ) ;
return ESP_OK ;
}
uint32_t essl_sdio_get_rx_data_size ( void * arg )
{
essl_sdio_context_t * ctx = arg ;
ESP_LOGV ( TAG , " rx latest: %d, read: %d " , ctx - > rx_got_bytes_latest , ctx - > rx_got_bytes ) ;
return ( ctx - > rx_got_bytes_latest + RX_BYTE_MAX - ctx - > rx_got_bytes ) % RX_BYTE_MAX ;
}
esp_err_t essl_sdio_update_rx_data_size ( void * arg , uint32_t wait_ms )
{
essl_sdio_context_t * ctx = arg ;
uint32_t len ;
esp_err_t err ;
ESP_LOGV ( TAG , " get_rx_data_size: got_bytes: %d " , ctx - > rx_got_bytes ) ;
err = essl_sdio_read_bytes ( ctx - > card , HOST_SLCHOST_PKT_LEN_REG , ( uint8_t * ) & len , 4 ) ;
if ( err ! = ESP_OK ) return err ;
len & = RX_BYTE_MASK ;
ctx - > rx_got_bytes_latest = len ;
return ESP_OK ;
}
esp_err_t essl_sdio_write_reg ( void * arg , uint8_t addr , uint8_t value , uint8_t * value_o , uint32_t wait_ms )
{
ESP_LOGV ( TAG , " write_reg: %08X " , value ) ;
// addrress over range
if ( addr > = 60 ) return ESP_ERR_INVALID_ARG ;
//W7 is reserved for interrupts
if ( addr > = 28 ) addr + = 4 ;
return essl_sdio_write_byte ( ( ( essl_sdio_context_t * ) arg ) - > card , HOST_SLCHOST_CONF_W_REG ( addr ) , value , value_o ) ;
}
esp_err_t essl_sdio_read_reg ( void * arg , uint8_t add , uint8_t * value_o , uint32_t wait_ms )
{
ESP_LOGV ( TAG , " read_reg " ) ;
// address over range
if ( add > = 60 ) return ESP_ERR_INVALID_ARG ;
//W7 is reserved for interrupts
if ( add > = 28 ) add + = 4 ;
esp_err_t ret = essl_sdio_read_byte ( ( ( essl_sdio_context_t * ) arg ) - > card , HOST_SLCHOST_CONF_W_REG ( add ) , value_o ) ;
ESP_LOGV ( TAG , " reg: %08X " , * value_o ) ;
return ret ;
}
esp_err_t essl_sdio_clear_intr ( void * arg , uint32_t intr_mask , uint32_t wait_ms )
{
ESP_LOGV ( TAG , " clear_intr: %08X " , intr_mask ) ;
return essl_sdio_write_bytes ( ( ( essl_sdio_context_t * ) arg ) - > card , HOST_SLC0HOST_INT_CLR_REG , ( uint8_t * ) & intr_mask , 4 ) ;
}
esp_err_t essl_sdio_get_intr ( void * arg , uint32_t * intr_raw , uint32_t * intr_st , uint32_t wait_ms )
{
essl_sdio_context_t * ctx = arg ;
esp_err_t r ;
ESP_LOGV ( TAG , " get_intr " ) ;
if ( intr_raw = = NULL & & intr_st = = NULL ) return ESP_ERR_INVALID_ARG ;
if ( intr_raw ! = NULL ) {
r = essl_sdio_read_bytes ( ctx - > card , HOST_SLC0HOST_INT_RAW_REG , ( uint8_t * ) intr_raw , 4 ) ;
if ( r ! = ESP_OK ) return r ;
}
if ( intr_st ! = NULL ) {
r = essl_sdio_read_bytes ( ctx - > card , HOST_SLC0HOST_INT_ST_REG , ( uint8_t * ) intr_st , 4 ) ;
if ( r ! = ESP_OK ) return r ;
}
return ESP_OK ;
}
esp_err_t essl_sdio_set_intr_ena ( void * arg , uint32_t ena_mask , uint32_t wait_ms )
{
ESP_LOGV ( TAG , " set_intr_ena: %08X " , ena_mask ) ;
return essl_sdio_write_bytes ( ( ( essl_sdio_context_t * ) arg ) - > card , HOST_SLC0HOST_FUNC1_INT_ENA_REG ,
( uint8_t * ) & ena_mask , 4 ) ;
}
esp_err_t essl_sdio_get_intr_ena ( void * arg , uint32_t * ena_mask_o , uint32_t wait_ms )
{
ESP_LOGV ( TAG , " get_intr_ena " ) ;
esp_err_t ret = essl_sdio_read_bytes ( ( ( essl_sdio_context_t * ) arg ) - > card , HOST_SLC0HOST_FUNC1_INT_ENA_REG ,
( uint8_t * ) ena_mask_o , 4 ) ;
ESP_LOGV ( TAG , " ena: %08X " , * ena_mask_o ) ;
return ret ;
}
esp_err_t essl_sdio_send_slave_intr ( void * arg , uint32_t intr_mask , uint32_t wait_ms )
{
ESP_LOGV ( TAG , " send_slave_intr: %02x " , intr_mask ) ;
return essl_sdio_write_byte ( ( ( essl_sdio_context_t * ) arg ) - > card , HOST_SLCHOST_CONF_W7_REG + 0 , intr_mask , NULL ) ;
}
esp_err_t essl_sdio_wait_int ( void * arg , uint32_t wait_ms )
{
return sdmmc_io_wait_int ( ( ( essl_sdio_context_t * ) arg ) - > card , wait_ms ) ;
}
void essl_sdio_reset_cnt ( void * arg )
{
essl_sdio_context_t * ctx = arg ;
ctx - > rx_got_bytes = 0 ;
ctx - > tx_sent_buffers = 0 ;
2020-07-31 10:26:07 +00:00
}
# endif // #if SOC_SDIO_SLAVE_SUPPORTED