Merge branch 'feature/spi_dma_hal_ll_refactor' into 'master'

spi: refactor DMA ll layer functions

Closes IDFGH-3538 and IDFGH-2555

See merge request espressif/esp-idf!9929
This commit is contained in:
Michael (XIAO Xufeng)
2020-09-23 15:32:41 +08:00
13 changed files with 876 additions and 536 deletions

View File

@@ -144,7 +144,7 @@ typedef struct {
int id;
spi_device_t* device[DEV_NUM_MAX];
intr_handle_t intr;
spi_hal_context_t hal;
spi_hal_context_t hal;
spi_trans_priv_t cur_trans_buf;
int cur_cs; //current device doing transaction
const spi_bus_attr_t* bus_attr;
@@ -164,9 +164,8 @@ struct spi_device_t {
QueueHandle_t trans_queue;
QueueHandle_t ret_queue;
spi_device_interface_config_t cfg;
spi_hal_timing_conf_t timing_conf;
spi_hal_dev_config_t hal_dev;
spi_host_t *host;
spi_bus_lock_dev_handle_t dev_lock;
};
@@ -230,11 +229,18 @@ static esp_err_t spi_master_init_driver(spi_host_device_t host_id)
}
}
spi_hal_init(&host->hal, host_id);
//assign the SPI, RX DMA and TX DMA peripheral registers beginning address
spi_hal_dma_config_t hal_dma_config = {
//On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. Pass the registers of SPI peripheral to control it.
.dma_in = SPI_LL_GET_HW(host_id),
.dma_out = SPI_LL_GET_HW(host_id),
.dmadesc_tx = bus_attr->dmadesc_tx,
.dmadesc_rx = bus_attr->dmadesc_rx,
.dmadesc_n = bus_attr->dma_desc_num
};
spi_hal_init(&host->hal, host_id, &hal_dma_config);
host->hal.dma_enabled = (bus_attr->dma_chan != 0);
host->hal.dmadesc_tx = bus_attr->dmadesc_tx;
host->hal.dmadesc_rx = bus_attr->dmadesc_rx;
host->hal.dmadesc_n = bus_attr->dma_desc_num;
if (host_id != SPI1_HOST) {
//SPI1 attributes are already initialized at start up.
@@ -293,7 +299,6 @@ void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dum
int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns)
{
return spi_hal_get_freq_limit(gpio_is_used, input_delay_ns);
}
/*
@@ -302,7 +307,6 @@ int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns)
*/
esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interface_config_t *dev_config, spi_device_handle_t *handle)
{
int duty_cycle;
spi_device_t *dev = NULL;
esp_err_t err = ESP_OK;
@@ -339,33 +343,32 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
int freecs = spi_bus_lock_get_dev_id(dev_handle);
SPI_CHECK(freecs != -1, "no free cs pins for the host", ESP_ERR_NOT_FOUND);
duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos;
//input parameters to calculate timing configuration
int half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0;
int no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0;
int duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos;
int use_gpio = !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS);
spi_hal_timing_param_t timing_param = {
.half_duplex = half_duplex,
.no_compensate = no_compensate,
.clock_speed_hz = dev_config->clock_speed_hz,
.duty_cycle = duty_cycle,
.input_delay_ns = dev_config->input_delay_ns,
.use_gpio = use_gpio
};
//output values of timing configuration
spi_hal_timing_conf_t temp_timing_conf;
int freq;
spi_hal_context_t *hal = &(host->hal);
hal->half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0;
#ifdef SOC_SPI_SUPPORT_AS_CS
hal->as_cs = dev_config->flags & SPI_DEVICE_CLK_AS_CS ? 1 : 0;
#endif
hal->positive_cs = dev_config->flags & SPI_DEVICE_POSITIVE_CS ? 1 : 0;
hal->no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0;
spi_hal_timing_conf_t temp_timing_conf;
esp_err_t ret = spi_hal_cal_clock_conf(hal, dev_config->clock_speed_hz, duty_cycle,
!(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS),
dev_config->input_delay_ns, &freq,
&temp_timing_conf);
esp_err_t ret = spi_hal_cal_clock_conf(&timing_param, &freq, &temp_timing_conf);
SPI_CHECK(ret==ESP_OK, "assigned clock speed not supported", ret);
//Allocate memory for device
dev=malloc(sizeof(spi_device_t));
if (dev==NULL) goto nomem;
dev = malloc(sizeof(spi_device_t));
if (dev == NULL) goto nomem;
memset(dev, 0, sizeof(spi_device_t));
host->device[freecs] = dev;
dev->id = freecs;
dev->timing_conf = temp_timing_conf;
dev->dev_lock = dev_handle;
//Allocate queues, set defaults
@@ -375,8 +378,6 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
goto nomem;
}
dev->host= host;
//We want to save a copy of the dev config in the dev struct.
memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
dev->cfg.duty_cycle_pos = duty_cycle;
@@ -384,10 +385,37 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
//Set CS pin, CS options
if (dev_config->spics_io_num >= 0) {
spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS));
spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, use_gpio);
}
*handle=dev;
//save a pointer to device in spi_host_t
host->device[freecs] = dev;
//save a pointer to host in spi_device_t
dev->host= host;
//initialise the device specific configuration
spi_hal_dev_config_t *hal_dev = &(dev->hal_dev);
hal_dev->mode = dev_config->mode;
hal_dev->cs_setup = dev_config->cs_ena_pretrans;
hal_dev->cs_hold = dev_config->cs_ena_posttrans;
//set hold_time to 0 will not actually append delay to CS
//set it to 1 since we do need at least one clock of hold time in most cases
if (hal_dev->cs_hold == 0) {
hal_dev->cs_hold = 1;
}
hal_dev->cs_pin_id = dev->id;
hal_dev->timing_conf = temp_timing_conf;
hal_dev->sio = (dev_config->flags) & SPI_DEVICE_3WIRE ? 1 : 0;
hal_dev->half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0;
hal_dev->tx_lsbfirst = dev_config->flags & SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0;
hal_dev->rx_lsbfirst = dev_config->flags & SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0;
hal_dev->no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0;
#ifdef SOC_SPI_SUPPORT_AS_CS
hal_dev->as_cs = dev_config->flags& SPI_DEVICE_CLK_AS_CS ? 1 : 0;
#endif
hal_dev->positive_cs = dev_config->flags & SPI_DEVICE_POSITIVE_CS ? 1 : 0;
*handle = dev;
ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %dkHz", host_id+1, freecs, freq/1000);
return ESP_OK;
@@ -447,24 +475,9 @@ static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev)
//if the configuration is already applied, skip the following.
return;
}
spi_host_t* host = dev->host;
spi_hal_context_t *hal = &host->hal;
hal->mode = dev->cfg.mode;
hal->tx_lsbfirst = dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0;
hal->rx_lsbfirst = dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0;
hal->no_compensate = dev->cfg.flags & SPI_DEVICE_NO_DUMMY ? 1 : 0;
hal->sio = dev->cfg.flags & SPI_DEVICE_3WIRE ? 1 : 0;
hal->dummy_bits = dev->cfg.dummy_bits;
hal->cs_setup = dev->cfg.cs_ena_pretrans;
hal->cs_hold =dev->cfg.cs_ena_posttrans;
//set hold_time to 0 will not actually append delay to CS
//set it to 1 since we do need at least one clock of hold time in most cases
if (hal->cs_hold == 0) hal->cs_hold = 1;
hal->cs_pin_id = dev->id;
hal->timing_conf = &dev->timing_conf;
spi_hal_setup_device(hal);
spi_hal_context_t *hal = &dev->host->hal;
spi_hal_dev_config_t *hal_dev = &(dev->hal_dev);
spi_hal_setup_device(hal, hal_dev);
}
static SPI_MASTER_ISR_ATTR spi_device_t *get_acquiring_dev(spi_host_t *host)
@@ -501,54 +514,53 @@ static void spi_bus_intr_disable(void *host)
// The function is called to send a new transaction, in ISR or in the task.
// Setup the transaction-specified registers and linked-list used by the DMA (or FIFO if DMA is not used)
static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf, spi_hal_context_t *hal)
static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf)
{
spi_transaction_t *trans = NULL;
spi_host_t *host = dev->host;
int dev_id = dev->id;
spi_hal_context_t *hal = &(host->hal);
spi_hal_dev_config_t *hal_dev = &(dev->hal_dev);
trans = trans_buf->trans;
host->cur_cs = dev_id;
host->cur_cs = dev->id;
//Reconfigure according to device settings, the function only has effect when the dev_id is changed.
spi_setup_device(host->device[dev_id]);
spi_setup_device(dev);
hal->tx_bitlen = trans->length;
hal->rx_bitlen = trans->rxlength;
hal->rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv;
hal->send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send;
hal->half_duplex = dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0;
hal->cmd = trans->cmd;
hal->addr = trans->addr;
//set the transaction specific configuration each time before a transaction setup
spi_hal_trans_config_t hal_trans = {};
hal_trans.tx_bitlen = trans->length;
hal_trans.rx_bitlen = trans->rxlength;
hal_trans.rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv;
hal_trans.send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send;
hal_trans.cmd = trans->cmd;
hal_trans.addr = trans->addr;
//Set up QIO/DIO if needed
hal->io_mode = (trans->flags & SPI_TRANS_MODE_DIO ?
hal_trans.io_mode = (trans->flags & SPI_TRANS_MODE_DIO ?
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_DIO : SPI_LL_IO_MODE_DUAL) :
(trans->flags & SPI_TRANS_MODE_QIO ?
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_QIO : SPI_LL_IO_MODE_QUAD) :
SPI_LL_IO_MODE_NORMAL
));
hal->tx_bitlen = trans->length;
hal->rx_bitlen = trans->rxlength;
if (trans->flags & SPI_TRANS_VARIABLE_CMD) {
hal->cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits;
hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits;
} else {
hal->cmd_bits = dev->cfg.command_bits;
hal_trans.cmd_bits = dev->cfg.command_bits;
}
if (trans->flags & SPI_TRANS_VARIABLE_ADDR) {
hal->addr_bits = ((spi_transaction_ext_t *)trans)->address_bits;
hal_trans.addr_bits = ((spi_transaction_ext_t *)trans)->address_bits;
} else {
hal->addr_bits = dev->cfg.address_bits;
hal_trans.addr_bits = dev->cfg.address_bits;
}
if (trans->flags & SPI_TRANS_VARIABLE_DUMMY) {
hal->dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits;
hal_trans.dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits;
} else {
hal->dummy_bits = dev->cfg.dummy_bits;
hal_trans.dummy_bits = dev->cfg.dummy_bits;
}
spi_hal_setup_trans(hal);
spi_hal_prepare_data(hal);
spi_hal_setup_trans(hal, hal_dev, &hal_trans);
spi_hal_prepare_data(hal, hal_dev, &hal_trans);
//Call pre-transmission callback, if any
if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans);
@@ -561,6 +573,7 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_
static void SPI_MASTER_ISR_ATTR spi_post_trans(spi_host_t *host)
{
spi_transaction_t *cur_trans = host->cur_trans_buf.trans;
spi_hal_fetch_result(&host->hal);
//Call post-transaction callback, if any
spi_device_t* dev = host->device[host->cur_cs];
@@ -569,7 +582,6 @@ static void SPI_MASTER_ISR_ATTR spi_post_trans(spi_host_t *host)
host->cur_cs = DEV_NUM_MAX;
}
// This is run in interrupt context.
static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
{
@@ -649,7 +661,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
//mark channel as active, so that the DMA will not be reset by the slave
spicommon_dmaworkaround_transfer_active(bus_attr->dma_chan);
}
spi_new_trans(device_to_send, cur_trans_buf, (&host->hal));
spi_new_trans(device_to_send, cur_trans_buf);
}
// Exit of the ISR, handle interrupt re-enable (if sending transaction), retry (if there's coming BG),
// or resume acquiring device task (if quit due to bus acquiring).
@@ -667,7 +679,7 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
bool rx_enabled = (trans_desc->flags & SPI_TRANS_USE_RXDATA) || (trans_desc->rx_buffer);
spi_transaction_ext_t *t_ext = (spi_transaction_ext_t *)trans_desc;
bool dummy_enabled = (((trans_desc->flags & SPI_TRANS_VARIABLE_DUMMY)? t_ext->dummy_bits: handle->cfg.dummy_bits) != 0);
bool extra_dummy_enabled = handle->timing_conf.timing_dummy;
bool extra_dummy_enabled = handle->hal_dev.timing_conf.timing_dummy;
bool is_half_duplex = ((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) != 0);
//check transmission length
@@ -763,7 +775,6 @@ clean_up:
return ESP_ERR_NO_MEM;
}
esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait)
{
esp_err_t ret = check_trans_valid(handle, trans_desc);
@@ -841,7 +852,6 @@ esp_err_t SPI_MASTER_ATTR spi_device_transmit(spi_device_handle_t handle, spi_tr
return ESP_OK;
}
esp_err_t SPI_MASTER_ISR_ATTR spi_device_acquire_bus(spi_device_t *device, TickType_t wait)
{
spi_host_t *const host = device->host;
@@ -897,7 +907,6 @@ void SPI_MASTER_ISR_ATTR spi_device_release_bus(spi_device_t *dev)
assert(ret == ESP_OK);
}
esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait)
{
esp_err_t ret;
@@ -923,12 +932,11 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handl
host->polling = true;
ESP_LOGV(SPI_TAG, "polling trans");
spi_new_trans(handle, &host->cur_trans_buf, (&host->hal));
spi_new_trans(handle, &host->cur_trans_buf);
return ESP_OK;
}
esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle, TickType_t ticks_to_wait)
{
SPI_CHECK(handle != NULL, "invalid dev handle", ESP_ERR_INVALID_ARG);
@@ -960,7 +968,6 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle,
return ESP_OK;
}
esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_transmit(spi_device_handle_t handle, spi_transaction_t* trans_desc)
{
esp_err_t ret;

View File

@@ -36,6 +36,7 @@
#include "esp_heap_caps.h"
#include "esp_rom_gpio.h"
#include "esp_rom_sys.h"
#include "hal/spi_slave_hal.h"
static const char *SPI_TAG = "spi_slave";
#define SPI_CHECK(a, str, ret_val) \
@@ -192,7 +193,13 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
}
spi_slave_hal_context_t *hal = &spihost[host]->hal;
spi_slave_hal_init(hal, host);
//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);
if (dma_desc_ct) {
hal->dmadesc_tx = heap_caps_malloc(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);

View File

@@ -102,13 +102,14 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
spi_slave_hd_hal_config_t hal_config = {
.host_id = host_id,
.dma_in = SPI_LL_GET_HW(host_id),
.dma_out = SPI_LL_GET_HW(host_id),
.tx_lsbfirst = (config->flags & SPI_SLAVE_HD_RXBIT_LSBFIRST),
.rx_lsbfirst = (config->flags & SPI_SLAVE_HD_TXBIT_LSBFIRST),
.dma_chan = config->dma_chan,
.mode = config->mode,
.mode = config->mode
};
slave_hd_hal_init(&host->hal, &hal_config);
spi_slave_hd_hal_init(&host->hal, &hal_config);
if (config->dma_chan != 0) {
//See how many dma descriptors we need and allocate them

View File

@@ -34,7 +34,7 @@ extern "C" {
#endif
/// Registers to reset during initialization. Don't use in app.
#define SPI_LL_RST_MASK (SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
#define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
/// Interrupt not used. Don't use in app.
#define SPI_LL_UNUSED_INT_MASK (SPI_INT_EN | SPI_SLV_WR_STA_DONE | SPI_SLV_RD_STA_DONE | SPI_SLV_WR_BUF_DONE | SPI_SLV_RD_BUF_DONE)
/// Swap the bit order to its correct place to send
@@ -49,6 +49,9 @@ extern "C" {
*/
typedef uint32_t spi_ll_clock_val_t;
//On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. So set the registers of SPI peripheral to control DMA.
typedef spi_dev_t spi_dma_dev_t;
/** IO modes supported by the master. */
typedef enum {
SPI_LL_IO_MODE_NORMAL = 0, ///< 1-bit mode for all phases
@@ -58,11 +61,6 @@ typedef enum {
SPI_LL_IO_MODE_QUAD, ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;
/// Interrupt type for different working pattern
typedef enum {
SPI_LL_INT_TYPE_NORMAL = 0, ///< Typical pattern, only wait for trans done
} spi_ll_slave_intr_type;
/*------------------------------------------------------------------------------
* Control
@@ -74,11 +72,6 @@ typedef enum {
*/
static inline void spi_ll_master_init(spi_dev_t *hw)
{
//Reset DMA
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
//Reset timing
hw->ctrl2.val = 0;
@@ -105,10 +98,6 @@ static inline void spi_ll_slave_init(spi_dev_t *hw)
hw->user.doutdin = 1; //we only support full duplex
hw->user.sio = 0;
hw->slave.slave_mode = 1;
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
hw->slave.sync_reset = 1;
hw->slave.sync_reset = 0;
//use all 64 bytes of the buffer
@@ -119,84 +108,6 @@ static inline void spi_ll_slave_init(spi_dev_t *hw)
hw->slave.val &= ~SPI_LL_UNUSED_INT_MASK;
}
/**
* Reset TX and RX DMAs.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_reset_dma(spi_dev_t *hw)
{
//Reset DMA peripheral
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
hw->dma_conf.out_data_burst_en = 1;
hw->dma_conf.indscr_burst_en = 1;
hw->dma_conf.outdscr_burst_en = 1;
}
/**
* Start RX DMA.
*
* @param hw Beginning address of the peripheral registers.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_ll_rxdma_start(spi_dev_t *hw, lldesc_t *addr)
{
hw->dma_in_link.addr = (int) addr & 0xFFFFF;
hw->dma_in_link.start = 1;
}
/**
* Start TX DMA.
*
* @param hw Beginning address of the peripheral registers.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_ll_txdma_start(spi_dev_t *hw, lldesc_t *addr)
{
hw->dma_out_link.addr = (int) addr & 0xFFFFF;
hw->dma_out_link.start = 1;
}
/**
* Write to SPI buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_send Data address to copy to the buffer.
* @param bitlen Length to copy, in bits.
*/
static inline void spi_ll_write_buffer(spi_dev_t *hw, const uint8_t *buffer_to_send, size_t bitlen)
{
for (int x = 0; x < bitlen; x += 32) {
//Use memcpy to get around alignment issues for txdata
uint32_t word;
memcpy(&word, &buffer_to_send[x / 8], 4);
hw->data_buf[(x / 32)] = word;
}
}
/**
* Read from SPI buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_rcv Address to copy buffer data to.
* @param bitlen Length to copy, in bits.
*/
static inline void spi_ll_read_buffer(spi_dev_t *hw, uint8_t *buffer_to_rcv, size_t bitlen)
{
for (int x = 0; x < bitlen; x += 32) {
//Do a memcpy to get around possible alignment issues in rx_buffer
uint32_t word = hw->data_buf[x / 32];
int len = bitlen - x;
if (len > 32) {
len = 32;
}
memcpy(&buffer_to_rcv[x / 8], &word, (len + 7) / 8);
}
}
/**
* Check whether user-defined transaction is done.
*
@@ -232,48 +143,110 @@ static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw)
}
/**
* Disable the trans_done interrupt.
* Reset SPI CPU FIFO
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_disable_int(spi_dev_t *hw)
static inline void spi_ll_cpu_fifo_reset(spi_dev_t *hw)
{
hw->slave.trans_inten = 0;
//This is not used in esp32
}
/**
* Clear the trans_done interrupt.
* Reset SPI DMA FIFO
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_clear_int_stat(spi_dev_t *hw)
static inline void spi_ll_dma_fifo_reset(spi_dev_t *hw)
{
hw->slave.trans_done = 0;
hw->dma_conf.val |= SPI_LL_DMA_FIFO_RST_MASK;
hw->dma_conf.val &= ~SPI_LL_DMA_FIFO_RST_MASK;
}
/**
* Set the trans_done interrupt.
*
* Clear in fifo full error
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_int_stat(spi_dev_t *hw)
static inline void spi_ll_infifo_full_clr(spi_dev_t *hw)
{
hw->slave.trans_done = 1;
//This is not used in esp32
}
/**
* Enable the trans_done interrupt.
*
* Clear out fifo empty error
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_enable_int(spi_dev_t *hw)
static inline void spi_ll_outfifo_empty_clr(spi_dev_t *hw)
{
hw->slave.trans_inten = 1;
//This is not used in esp32
}
static inline void spi_ll_slave_set_int_type(spi_dev_t *hw, spi_ll_slave_intr_type int_type)
/*------------------------------------------------------------------------------
* SPI configuration for DMA
*----------------------------------------------------------------------------*/
/**
* Enable/Disable RX DMA (Peripherals->DMA->RAM)
*
* @param hw Beginning address of the peripheral registers.
* @param enable 1: enable; 2: disable
*/
static inline void spi_ll_dma_rx_enable(spi_dev_t *hw, bool enable)
{
hw->slave.trans_inten = 1;
//This is not used in esp32
}
/**
* Enable/Disable TX DMA (RAM->DMA->Peripherals)
*
* @param hw Beginning address of the peripheral registers.
* @param enable 1: enable; 2: disable
*/
static inline void spi_ll_dma_tx_enable(spi_dev_t *hw, bool enable)
{
//This is not used in esp32
}
/*------------------------------------------------------------------------------
* Buffer
*----------------------------------------------------------------------------*/
/**
* Write to SPI buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_send Data address to copy to the buffer.
* @param bitlen Length to copy, in bits.
*/
static inline void spi_ll_write_buffer(spi_dev_t *hw, const uint8_t *buffer_to_send, size_t bitlen)
{
for (int x = 0; x < bitlen; x += 32) {
//Use memcpy to get around alignment issues for txdata
uint32_t word;
memcpy(&word, &buffer_to_send[x / 8], 4);
hw->data_buf[(x / 32)] = word;
}
}
/**
* Read from SPI buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_rcv Address to copy buffer data to.
* @param bitlen Length to copy, in bits.
*/
static inline void spi_ll_read_buffer(spi_dev_t *hw, uint8_t *buffer_to_rcv, size_t bitlen)
{
for (int x = 0; x < bitlen; x += 32) {
//Do a memcpy to get around possible alignment issues in rx_buffer
uint32_t word = hw->data_buf[x / 32];
int len = bitlen - x;
if (len > 32) {
len = 32;
}
memcpy(&buffer_to_rcv[x / 8], &word, (len + 7) / 8);
}
}
/*------------------------------------------------------------------------------
@@ -485,7 +458,7 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers.
* @param val stored clock configuration calculated before (by ``spi_ll_cal_clock``).
*/
static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, spi_ll_clock_val_t *val)
static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_clock_val_t *val)
{
hw->clock.val = *(uint32_t *)val;
}
@@ -875,6 +848,167 @@ static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
return hw->slv_rd_bit.slv_rdata_bit;
}
/*------------------------------------------------------------------------------
* Interrupts
*----------------------------------------------------------------------------*/
/**
* Disable the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_disable_int(spi_dev_t *hw)
{
hw->slave.trans_inten = 0;
}
/**
* Clear the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_clear_int_stat(spi_dev_t *hw)
{
hw->slave.trans_done = 0;
}
/**
* Set the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_int_stat(spi_dev_t *hw)
{
hw->slave.trans_done = 1;
}
/**
* Enable the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_enable_int(spi_dev_t *hw)
{
hw->slave.trans_inten = 1;
}
/*------------------------------------------------------------------------------
* DMA:
* RX DMA (Peripherals->DMA->RAM)
* TX DMA (RAM->DMA->Peripherals)
*----------------------------------------------------------------------------*/
/**
* Reset RX DMA which stores the data received from a peripheral into RAM.
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
*/
static inline void spi_dma_ll_rx_reset(spi_dma_dev_t *dma_in)
{
//Reset RX DMA peripheral
dma_in->dma_conf.in_rst = 1;
dma_in->dma_conf.in_rst = 0;
}
/**
* Start RX DMA.
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_dma_ll_rx_start(spi_dma_dev_t *dma_in, lldesc_t *addr)
{
dma_in->dma_in_link.addr = (int) addr & 0xFFFFF;
dma_in->dma_in_link.start = 1;
}
/**
* Enable DMA RX channel burst for data
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_rx_enable_burst_data(spi_dma_dev_t *dma_out, bool enable)
{
//This is not supported in esp32
}
/**
* Enable DMA RX channel burst for descriptor
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_rx_enable_burst_desc(spi_dma_dev_t *dma_in, bool enable)
{
dma_in->dma_conf.indscr_burst_en = enable;
}
/**
* Configuration of RX DMA EOF interrupt generation way
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param enable 1: spi_dma_inlink_eof is set when the number of dma pushed data bytes is equal to the value of spi_slv/mst_dma_rd_bytelen[19:0] in spi dma transition. 0: spi_dma_inlink_eof is set by spi_trans_done in non-seg-trans or spi_dma_seg_trans_done in seg-trans.
*/
static inline void spi_dma_ll_set_rx_eof_generation(spi_dma_dev_t *dma_in, bool enable)
{
//does not available in ESP32
}
/**
* Reset TX DMA which transmits the data from RAM to a peripheral.
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
*/
static inline void spi_dma_ll_tx_reset(spi_dma_dev_t *dma_out)
{
//Reset TX DMA peripheral
dma_out->dma_conf.out_rst = 1;
dma_out->dma_conf.out_rst = 0;
}
/**
* Start TX DMA.
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_dma_ll_tx_start(spi_dma_dev_t *dma_out, lldesc_t *addr)
{
dma_out->dma_out_link.addr = (int) addr & 0xFFFFF;
dma_out->dma_out_link.start = 1;
}
/**
* Enable DMA TX channel burst for data
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_tx_enable_burst_data(spi_dma_dev_t *dma_out, bool enable)
{
dma_out->dma_conf.out_data_burst_en = enable;
}
/**
* Enable DMA TX channel burst for descriptor
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_tx_enable_burst_desc(spi_dma_dev_t *dma_out, bool enable)
{
dma_out->dma_conf.outdscr_burst_en = enable;
}
/**
* Enable automatic outlink-writeback
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_enable_out_auto_wrback(spi_dma_dev_t *dma_out, bool enable)
{
//does not configure it in ESP32
}
#undef SPI_LL_RST_MASK
#undef SPI_LL_UNUSED_INT_MASK

View File

@@ -35,7 +35,7 @@ extern "C" {
#endif
/// Registers to reset during initialization. Don't use in app.
#define SPI_LL_RST_MASK (SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
#define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
/// Interrupt not used. Don't use in app.
#define SPI_LL_UNUSED_INT_MASK (SPI_INT_TRANS_DONE_EN | SPI_INT_WR_DMA_DONE_EN | SPI_INT_RD_DMA_DONE_EN | SPI_INT_WR_BUF_DONE_EN | SPI_INT_RD_BUF_DONE_EN)
/// Swap the bit order to its correct place to send
@@ -50,6 +50,9 @@ extern "C" {
*/
typedef uint32_t spi_ll_clock_val_t;
//On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. So set the registers of SPI peripheral to control DMA.
typedef spi_dev_t spi_dma_dev_t;
/** IO modes supported by the master. */
typedef enum {
SPI_LL_IO_MODE_NORMAL = 0, ///< 1-bit mode for all phases
@@ -59,12 +62,6 @@ typedef enum {
SPI_LL_IO_MODE_QUAD, ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;
/// Interrupt type for different working pattern
typedef enum {
SPI_LL_INT_TYPE_NORMAL = 0, ///< Typical pattern, only wait for trans done
SPI_LL_INT_TYPE_SEG = 1, ///< Wait for DMA signals
} spi_ll_slave_intr_type;
/// Type definition of all supported interrupts
typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@@ -104,11 +101,6 @@ FLAG_ATTR(spi_ll_trans_len_cond_t)
*/
static inline void spi_ll_master_init(spi_dev_t *hw)
{
//Reset DMA
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
//Reset timing
hw->ctrl2.val = 0;
@@ -137,52 +129,26 @@ static inline void spi_ll_slave_init(spi_dev_t *hw)
hw->user.doutdin = 1; //we only support full duplex
hw->user.sio = 0;
hw->slave.slave_mode = 1;
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
hw->slave.soft_reset = 1;
hw->slave.soft_reset = 0;
//use all 64 bytes of the buffer
hw->user.usr_miso_highpart = 0;
hw->user.usr_mosi_highpart = 0;
//by default seg mode is disabled
hw->dma_conf.dma_continue = 0;
//Disable unneeded ints
hw->slave.val &= ~SPI_LL_UNUSED_INT_MASK;
hw->dma_int_ena.val = 0;
}
static inline void spi_ll_slave_hd_init(spi_dev_t* hw)
static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
{
hw->clock.val = 0;
hw->user.val = 0;
hw->ctrl.val = 0;
hw->user.sio = 0;
//hw->user.tx_start_bit = 7;
hw->slave.soft_reset = 1;
hw->slave.soft_reset = 0;
//Reset DMA
hw->dma_conf.val |= SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~(SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST);
if (hw == &GPSPI2) {
hw->dma_conf.out_data_burst_en = 1;
} else {
hw->dma_conf.out_data_burst_en = 0;
}
hw->dma_conf.outdscr_burst_en = 1;
hw->dma_conf.indscr_burst_en = 1;
hw->dma_conf.rx_eof_en = 0;
hw->dma_conf.out_eof_mode = 1;
hw->dma_conf.out_auto_wrback = 1;
hw->user.doutdin = 0; //we only support full duplex
hw->slave.slave_mode = 1;
}
@@ -221,103 +187,83 @@ static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw)
return hw->cmd.val;
}
/*------------------------------------------------------------------------------
* DMA
*----------------------------------------------------------------------------*/
/**
* Reset TX and RX DMAs.
* Reset SPI CPU FIFO
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_reset_dma(spi_dev_t *hw)
static inline void spi_ll_cpu_fifo_reset(spi_dev_t *hw)
{
//Reset DMA peripheral
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
hw->dma_conf.out_data_burst_en = 0;
hw->dma_conf.indscr_burst_en = 1;
hw->dma_conf.outdscr_burst_en = 1;
hw->dma_in_link.dma_rx_ena = 0;
assert(hw->dma_in_link.dma_rx_ena == 0);
//This is not used in esp32s2
}
/**
* Start RX DMA.
* Reset SPI DMA FIFO
*
* @param hw Beginning address of the peripheral registers.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_ll_rxdma_start(spi_dev_t *hw, lldesc_t *addr)
static inline void spi_ll_dma_fifo_reset(spi_dev_t *hw)
{
hw->dma_in_link.addr = (int) addr & 0xFFFFF;
hw->dma_in_link.start = 1;
hw->dma_conf.val |= SPI_LL_DMA_FIFO_RST_MASK;
hw->dma_conf.val &= ~SPI_LL_DMA_FIFO_RST_MASK;
}
/**
* Start TX DMA.
*
* Clear in fifo full error
*
* @param hw Beginning address of the peripheral registers.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_ll_txdma_start(spi_dev_t *hw, lldesc_t *addr)
static inline void spi_ll_infifo_full_clr(spi_dev_t *hw)
{
hw->dma_out_link.addr = (int) addr & 0xFFFFF;
hw->dma_out_link.start = 1;
}
static inline void spi_ll_rxdma_reset(spi_dev_t* hw)
{
hw->dma_conf.in_rst = 1;
hw->dma_conf.in_rst = 0;
hw->dma_conf.infifo_full_clr = 1;
hw->dma_conf.infifo_full_clr = 0;
}
static inline void spi_ll_txdma_reset(spi_dev_t* hw)
/**
* Clear out fifo empty error
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_outfifo_empty_clr(spi_dev_t *hw)
{
hw->dma_conf.out_rst = 1;
hw->dma_conf.out_rst = 0;
hw->dma_conf.outfifo_empty_clr = 1;
hw->dma_conf.outfifo_empty_clr = 0;
}
static inline void spi_ll_rxdma_restart(spi_dev_t* hw)
/*------------------------------------------------------------------------------
* SPI configuration for DMA
*----------------------------------------------------------------------------*/
/**
* Enable/Disable RX DMA (Peripherals->DMA->RAM)
*
* @param hw Beginning address of the peripheral registers.
* @param enable 1: enable; 2: disable
*/
static inline void spi_ll_dma_rx_enable(spi_dev_t *hw, bool enable)
{
hw->dma_in_link.restart = 1;
//This is not used in esp32s2
}
static inline void spi_ll_txdma_restart(spi_dev_t* hw)
/**
* Enable/Disable TX DMA (RAM->DMA->Peripherals)
*
* @param hw Beginning address of the peripheral registers.
* @param enable 1: enable; 2: disable
*/
static inline void spi_ll_dma_tx_enable(spi_dev_t *hw, bool enable)
{
hw->dma_out_link.restart = 1;
//This is not used in esp32s2
}
static inline void spi_ll_rxdma_disable(spi_dev_t* hw)
/**
* Configuration of OUT EOF flag generation way
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param enable 1: when dma pop all data from fifo 0:when ahb push all data to fifo.
*/
static inline void spi_ll_dma_set_out_eof_generation(spi_dma_dev_t *dma_out, bool enable)
{
hw->dma_in_link.dma_rx_ena = 0;
}
static inline void spi_ll_txdma_disable(spi_dev_t* hw)
{
hw->dma_out_link.dma_tx_ena = 0;
hw->dma_out_link.stop = 1;
}
static inline void spi_ll_rxdma_clr_err(spi_dev_t* hw)
{
hw->dma_conf.infifo_full_clr = 1;
hw->dma_conf.infifo_full_clr = 0;
}
static inline void spi_ll_txdma_clr_err(spi_dev_t* hw)
{
hw->dma_int_clr.outfifo_empty_err= 1;
}
static inline bool spi_ll_txdma_get_empty_err(spi_dev_t* hw)
{
return hw->dma_int_raw.outfifo_empty_err;
dma_out->dma_conf.out_eof_mode = enable;
}
/*------------------------------------------------------------------------------
@@ -559,7 +505,7 @@ static inline void spi_ll_master_set_io_mode(spi_dev_t *hw, spi_ll_io_mode_t io_
}
}
static inline void spi_ll_slave_set_seg_mode(spi_dev_t* hw, bool seg_trans)
static inline void spi_ll_slave_set_seg_mode(spi_dev_t *hw, bool seg_trans)
{
hw->dma_conf.dma_seg_trans_en = seg_trans;
hw->dma_conf.rx_eof_en = seg_trans;
@@ -590,7 +536,7 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers.
* @param val stored clock configuration calculated before (by ``spi_ll_cal_clock``).
*/
static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, spi_ll_clock_val_t *val)
static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_clock_val_t *val)
{
hw->clock.val = *(uint32_t *)val;
}
@@ -784,7 +730,7 @@ static inline void spi_ll_master_set_cs_setup(spi_dev_t *hw, uint8_t setup)
* Enable/disable the segment transfer feature for the slave.
*
* @param hw Beginning address of the peripheral registers.
* @param en true to enable, false to disable.
* @param en true to enable, false to disable.
*/
static inline void spi_ll_slave_set_seg_en(spi_dev_t *hw, bool en)
{
@@ -982,7 +928,7 @@ static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
item(SPI_LL_INTR_OUT_EOF, dma_int_ena.out_eof, dma_int_raw.out_eof, dma_int_clr.out_eof=1) \
item(SPI_LL_INTR_OUT_TOTAL_EOF, dma_int_ena.out_total_eof, dma_int_raw.out_total_eof, dma_int_clr.out_total_eof=1) \
item(SPI_LL_INTR_SEG_DONE, slave.int_dma_seg_trans_en, hold.dma_seg_trans_done, hold.dma_seg_trans_done=0) \
item(SPI_LL_INTR_IN_FULL, dma_int_ena.infifo_full_err, dma_int_raw.infifo_full_err, dma_int_clr.infifo_full_err=1) \
item(SPI_LL_INTR_IN_FULL, dma_int_ena.infifo_full_err, dma_int_raw.infifo_full_err, dma_int_clr.infifo_full_err=1) \
item(SPI_LL_INTR_OUT_EMPTY, dma_int_ena.outfifo_empty_err, dma_int_raw.outfifo_empty_err, dma_int_clr.outfifo_empty_err=1) \
item(SPI_LL_INTR_WR_DONE, dma_int_ena.cmd7, dma_int_raw.cmd7, dma_int_clr.cmd7=1) \
item(SPI_LL_INTR_CMD8, dma_int_ena.cmd8, dma_int_raw.cmd8, dma_int_clr.cmd8=1) \
@@ -1047,7 +993,6 @@ static inline void spi_ll_disable_int(spi_dev_t *hw)
static inline void spi_ll_clear_int_stat(spi_dev_t *hw)
{
hw->slave.trans_done = 0;
hw->dma_int_clr.val = UINT32_MAX;
}
/**
@@ -1070,27 +1015,6 @@ static inline void spi_ll_enable_int(spi_dev_t *hw)
hw->slave.int_trans_done_en = 1;
}
/**
* Set different interrupt types for the slave.
*
* @param hw Beginning address of the peripheral registers.
* @param int_type Interrupt type
*/
static inline void spi_ll_slave_set_int_type(spi_dev_t *hw, spi_ll_slave_intr_type int_type)
{
switch (int_type) {
case SPI_LL_INT_TYPE_SEG:
hw->dma_int_ena.in_suc_eof = 1;
hw->dma_int_ena.out_total_eof = 1;
hw->slave.int_trans_done_en = 0;
break;
default:
hw->dma_int_ena.in_suc_eof = 0;
hw->dma_int_ena.out_total_eof = 0;
hw->slave.int_trans_done_en = 1;
}
}
/*------------------------------------------------------------------------------
* Slave HD
*----------------------------------------------------------------------------*/
@@ -1111,6 +1035,157 @@ static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t* hw)
{
return hw->slave1.last_addr;
}
/*------------------------------------------------------------------------------
* DMA:
* RX DMA (Peripherals->DMA->RAM)
* TX DMA (RAM->DMA->Peripherals)
*----------------------------------------------------------------------------*/
/**
* Reset RX DMA which stores the data received from a peripheral into RAM.
*
* @param hw Beginning address of the peripheral registers.
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
*/
static inline void spi_dma_ll_rx_reset(spi_dma_dev_t *dma_in)
{
//Reset RX DMA peripheral
dma_in->dma_in_link.dma_rx_ena = 0;
assert(dma_in->dma_in_link.dma_rx_ena == 0);
dma_in->dma_conf.in_rst = 1;
dma_in->dma_conf.in_rst = 0;
}
/**
* Start RX DMA.
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_dma_ll_rx_start(spi_dma_dev_t *dma_in, lldesc_t *addr)
{
dma_in->dma_in_link.addr = (int) addr & 0xFFFFF;
dma_in->dma_in_link.start = 1;
}
/**
* Enable DMA RX channel burst for data
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_rx_enable_burst_data(spi_dma_dev_t *dma_out, bool enable)
{
//This is not supported in esp32s2
}
/**
* Enable DMA TX channel burst for descriptor
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_rx_enable_burst_desc(spi_dma_dev_t *dma_in, bool enable)
{
dma_in->dma_conf.indscr_burst_en = enable;
}
/**
* Configuration of RX DMA EOF interrupt generation way
*
* @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
* @param enable 1: spi_dma_inlink_eof is set when the number of dma pushed data bytes is equal to the value of spi_slv/mst_dma_rd_bytelen[19:0] in spi dma transition. 0: spi_dma_inlink_eof is set by spi_trans_done in non-seg-trans or spi_dma_seg_trans_done in seg-trans.
*/
static inline void spi_dma_ll_set_rx_eof_generation(spi_dma_dev_t *dma_in, bool enable)
{
dma_in->dma_conf.rx_eof_en = enable;
}
/**
* Reset TX DMA which transmits the data from RAM to a peripheral.
*
* @param hw Beginning address of the peripheral registers.
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
*/
static inline void spi_dma_ll_tx_reset(spi_dma_dev_t *dma_out)
{
//Reset TX DMA peripheral
dma_out->dma_conf.out_rst = 1;
dma_out->dma_conf.out_rst = 0;
}
/**
* Start TX DMA.
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_dma_ll_tx_start(spi_dma_dev_t *dma_out, lldesc_t *addr)
{
dma_out->dma_out_link.addr = (int) addr & 0xFFFFF;
dma_out->dma_out_link.start = 1;
}
/**
* Enable DMA TX channel burst for data
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_tx_enable_burst_data(spi_dma_dev_t *dma_out, bool enable)
{
dma_out->dma_conf.out_data_burst_en = enable;
}
/**
* Enable DMA TX channel burst for descriptor
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_tx_enable_burst_desc(spi_dma_dev_t *dma_out, bool enable)
{
dma_out->dma_conf.outdscr_burst_en = enable;
}
/**
* Enable automatic outlink-writeback
*
* @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
* @param enable True to enable, false to disable
*/
static inline void spi_dma_ll_enable_out_auto_wrback(spi_dma_dev_t *dma_out, bool enable)
{
dma_out->dma_conf.out_auto_wrback = enable;
}
static inline void spi_dma_ll_rx_restart(spi_dma_dev_t *dma_in)
{
dma_in->dma_in_link.restart = 1;
}
static inline void spi_dma_ll_tx_restart(spi_dma_dev_t *dma_out)
{
dma_out->dma_out_link.restart = 1;
}
static inline void spi_dma_ll_rx_disable(spi_dma_dev_t *dma_in)
{
dma_in->dma_in_link.dma_rx_ena = 0;
}
static inline void spi_dma_ll_tx_disable(spi_dma_dev_t *dma_out)
{
dma_out->dma_out_link.dma_tx_ena = 0;
dma_out->dma_out_link.stop = 1;
}
static inline bool spi_ll_tx_get_empty_err(spi_dev_t *hw)
{
return hw->dma_int_raw.outfifo_empty_err;
}
#undef SPI_LL_RST_MASK
#undef SPI_LL_UNUSED_INT_MASK

View File

@@ -38,83 +38,115 @@
#include <esp_err.h>
#include "soc/lldesc.h"
/**
* Input parameters to the ``spi_hal_cal_clock_conf`` to calculate the timing configuration
*/
typedef struct {
uint32_t half_duplex; ///< Whether half duplex mode is used, device specific
uint32_t no_compensate; ///< No need to add dummy to compensate the timing, device specific
uint32_t clock_speed_hz; ///< Desired frequency.
uint32_t duty_cycle; ///< Desired duty cycle of SPI clock
uint32_t input_delay_ns; /**< Maximum delay between SPI launch clock and the data to be valid.
* This is used to compensate/calculate the maximum frequency allowed.
* Left 0 if not known.
*/
bool use_gpio; ///< True if the GPIO matrix is used, otherwise false
} spi_hal_timing_param_t;
/**
* Timing configuration structure that should be calculated by
* ``spi_hal_setup_clock`` at initialization and hold. Filled into the
* ``spi_hal_cal_clock_conf`` at initialization and hold. Filled into the
* ``timing_conf`` member of the context of HAL before setup a device.
*/
typedef struct {
spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer
int timing_dummy; ///< Extra dummy needed to compensate the timing
int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing
spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer
int timing_dummy; ///< Extra dummy needed to compensate the timing
int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing
} spi_hal_timing_conf_t;
/**
* DMA configuration structure
* Should be set by driver at initialization
*/
typedef struct {
spi_dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address
spi_dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use.
} spi_hal_dma_config_t;
/**
* Transaction configuration structure, this should be assigned by driver each time.
* All these parameters will be updated to the peripheral every transaction.
*/
typedef struct {
uint16_t cmd; ///< Command value to be sent
int cmd_bits; ///< Length (in bits) of the command phase
int addr_bits; ///< Length (in bits) of the address phase
int dummy_bits; ///< Base length (in bits) of the dummy phase. Note when the compensation is enabled, some extra dummy bits may be appended.
int tx_bitlen; ///< TX length, in bits
int rx_bitlen; ///< RX length, in bits
uint64_t addr; ///< Address value to be sent
uint8_t *send_buffer; ///< Data to be sent
uint8_t *rcv_buffer; ///< Buffer to hold the receive data.
spi_ll_io_mode_t io_mode; ///< IO mode of the master
} spi_hal_trans_config_t;
/**
* Context that should be maintained by both the driver and the HAL.
*/
typedef struct {
/* configured by driver at initialization, don't touch */
spi_dev_t *hw; ///< Beginning address of the peripheral registers.
/* should be configured by driver at initialization */
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use.
/*
* Device specific, all these parameters will be updated to the peripheral
* only when ``spi_hal_setup_device``. They may not get updated when
* ``spi_hal_setup_trans``.
*/
/* Configured by driver at initialization, don't touch */
spi_dev_t *hw; ///< Beginning address of the peripheral registers.
spi_dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM (DMA -> RAM).
spi_dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral (RAM -> DMA).
bool dma_enabled; ///< Whether the DMA is enabled, do not update after initialization
spi_hal_dma_config_t dma_config; ///< DMA configuration
/* Internal parameters, don't touch */
spi_hal_trans_config_t trans_config; ///< Transaction configuration
} spi_hal_context_t;
/**
* Device configuration structure, this should be initialised by driver based on different devices respectively.
* All these parameters will be updated to the peripheral only when ``spi_hal_setup_device``.
* They may not get updated when ``spi_hal_setup_trans``.
*/
typedef struct {
int mode; ///< SPI mode, device specific
int cs_setup; ///< Setup time of CS active edge before the first SPI clock, device specific
int cs_hold; ///< Hold time of CS inactive edge after the last SPI clock, device specific
int cs_pin_id; ///< CS pin to use, 0-2, otherwise all the CS pins are not used. Device specific
spi_hal_timing_conf_t *timing_conf; /**< Pointer to an structure holding
* the pre-calculated timing configuration for the device at initialization,
* device specific
spi_hal_timing_conf_t timing_conf; /**< This structure holds the pre-calculated timing configuration for the device
* at initialization, device specific
*/
struct {
uint32_t sio : 1; ///< Whether to use SIO mode, device specific
uint32_t half_duplex : 1; ///< Whether half duplex mode is used, device specific
uint32_t tx_lsbfirst : 1; ///< Whether LSB is sent first for TX data, device specific
uint32_t rx_lsbfirst : 1; ///< Whether LSB is received first for RX data, device specific
uint32_t dma_enabled : 1; ///< Whether the DMA is enabled, do not update after initialization
uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific
uint32_t sio : 1; ///< Whether to use SIO mode, device specific
uint32_t half_duplex : 1; ///< Whether half duplex mode is used, device specific
uint32_t tx_lsbfirst : 1; ///< Whether LSB is sent first for TX data, device specific
uint32_t rx_lsbfirst : 1; ///< Whether LSB is received first for RX data, device specific
uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific
#ifdef SOC_SPI_SUPPORT_AS_CS
uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific
uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific
#endif
uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific
uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific
};//boolean configurations
/*
* Transaction specific (data), all these parameters will be updated to the
* peripheral every transaction.
*/
uint16_t cmd; ///< Command value to be sent
int cmd_bits; ///< Length (in bits) of the command phase
int addr_bits; ///< Length (in bits) of the address phase
int dummy_bits; ///< Base length (in bits) of the dummy phase. Note when the compensation is enabled, some extra dummy bits may be appended.
int tx_bitlen; ///< TX length, in bits
int rx_bitlen; ///< RX length, in bits
uint64_t addr; ///< Address value to be sent
uint8_t *send_buffer; ///< Data to be sent
uint8_t *rcv_buffer; ///< Buffer to hold the receive data.
spi_ll_io_mode_t io_mode; ///< IO mode of the master
} spi_hal_context_t;
} spi_hal_dev_config_t;
/**
* Init the peripheral and the context.
*
* @param hal Context of the HAL layer.
* @param hal Context of the HAL layer.
* @param host_id Index of the SPI peripheral. 0 for SPI1, 1 for HSPI (SPI2) and 2 for VSPI (SPI3).
*/
void spi_hal_init(spi_hal_context_t *hal, int host_id);
void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_dma_config_t *hal_dma_config);
/**
* Deinit the peripheral (and the context if needed).
@@ -126,23 +158,28 @@ void spi_hal_deinit(spi_hal_context_t *hal);
/**
* Setup device-related configurations according to the settings in the context.
*
* @param hal Context of the HAL layer.
* @param hal Context of the HAL layer.
* @param hal_dev Device configuration
*/
void spi_hal_setup_device(const spi_hal_context_t *hal);
void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev);
/**
* Setup transaction related configurations according to the settings in the context.
*
* @param hal Context of the HAL layer.
* @param hal Context of the HAL layer.
* @param hal_dev Device configuration
* @param hal_trans Transaction configuration
*/
void spi_hal_setup_trans(const spi_hal_context_t *hal);
void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans);
/**
* Prepare the data for the current transaction.
*
* @param hal Context of the HAL layer.
* @param hal Context of the HAL layer.
* @param hal_dev Device configuration
* @param hal_trans Transaction configuration
*/
void spi_hal_prepare_data(const spi_hal_context_t *hal);
void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans);
/**
* Trigger start a user-defined transaction.
@@ -161,7 +198,7 @@ bool spi_hal_usr_is_done(const spi_hal_context_t *hal);
/**
* Post transaction operations, mainly fetch data from the buffer.
*
* @param hal Context of the HAL layer.
* @param hal Context of the HAL layer.
*/
void spi_hal_fetch_result(const spi_hal_context_t *hal);
@@ -173,50 +210,44 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal);
*
* It is highly suggested to do this at initialization, since it takes long time.
*
* @param hal Context of the HAL layer.
* @param speed_hz Desired frequency.
* @param duty_cycle Desired duty cycle of SPI clock
* @param use_gpio true if the GPIO matrix is used, otherwise false
* @param input_delay_ns Maximum delay between SPI launch clock and the data to
* be valid. This is used to compensate/calculate the maximum frequency
* allowed. Left 0 if not known.
* @param out_freq Output of the actual frequency, left NULL if not required.
* @param timing_conf Output of the timing configuration.
* @param timing_param Input parameters to calculate timing configuration
* @param out_freq Output of the actual frequency, left NULL if not required.
* @param timing_conf Output of the timing configuration.
*
* @return ESP_OK if desired is available, otherwise fail.
*/
esp_err_t spi_hal_cal_clock_conf(const spi_hal_context_t *hal, int speed_hz, int duty_cycle, bool use_gpio, int input_delay_ns, int *out_freq, spi_hal_timing_conf_t *timing_conf);
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf);
/**
* Get the frequency actual used.
*
* @param hal Context of the HAL layer.
* @param fapb APB clock frequency.
* @param hz Desired frequencyc.
* @param duty_cycle Desired duty cycle.
* @param hal Context of the HAL layer.
* @param fapb APB clock frequency.
* @param hz Desired frequencyc.
* @param duty_cycle Desired duty cycle.
*/
int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle);
/**
* Get the timing configuration for given parameters.
*
* @param eff_clk Actual SPI clock frequency
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
* @param eff_clk Actual SPI clock frequency
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
* @param input_delay_ns Maximum delay between SPI launch clock and the data to
* be valid. This is used to compensate/calculate the maximum frequency
* allowed. Left 0 if not known.
* @param dummy_n Dummy cycles required to correctly read the data.
* @param miso_delay_n suggested delay on the MISO line, in APB clocks.
* be valid. This is used to compensate/calculate the maximum frequency
* allowed. Left 0 if not known.
* @param dummy_n Dummy cycles required to correctly read the data.
* @param miso_delay_n suggested delay on the MISO line, in APB clocks.
*/
void spi_hal_cal_timing(int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n);
/**
* Get the maximum frequency allowed to read if no compensation is used.
*
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
* @param input_delay_ns Maximum delay between SPI launch clock and the data to
* be valid. This is used to compensate/calculate the maximum frequency
* allowed. Left 0 if not known.
* be valid. This is used to compensate/calculate the maximum frequency
* allowed. Left 0 if not known.
*/
int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns);

View File

@@ -36,32 +36,35 @@
#include "soc/spi_struct.h"
#include <esp_types.h>
#include "soc/spi_caps.h"
#include "hal/spi_ll.h"
/**
* Context that should be maintained by both the driver and the HAL.
*/
typedef struct {
/* configured by driver at initialization, don't touch */
spi_dev_t *hw; ///< Beginning address of the peripheral registers.
spi_dev_t *hw; ///< Beginning address of the peripheral registers.
spi_dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
spi_dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
/* should be configured by driver at initialization */
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the TX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the RX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use.
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the TX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the RX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use.
/*
* configurations to be filled after ``spi_slave_hal_init``. Updated to
* peripheral registers when ``spi_slave_hal_setup_device`` is called.
*/
struct {
uint32_t rx_lsbfirst : 1;
uint32_t tx_lsbfirst : 1;
uint32_t use_dma : 1;
uint32_t rx_lsbfirst : 1;
uint32_t tx_lsbfirst : 1;
uint32_t use_dma : 1;
};
int mode;
@@ -69,21 +72,27 @@ typedef struct {
* Transaction specific (data), all these parameters will be updated to the
* peripheral every transaction.
*/
uint32_t bitlen; ///< Expected maximum length of the transaction, in bits.
const void *tx_buffer; ///< Data to be sent
void *rx_buffer; ///< Buffer to hold the received data.
uint32_t bitlen; ///< Expected maximum length of the transaction, in bits.
const void *tx_buffer; ///< Data to be sent
void *rx_buffer; ///< Buffer to hold the received data.
/* Other transaction result after one transaction */
uint32_t rcv_bitlen; ///< Length of the last transaction, in bits.
uint32_t rcv_bitlen; ///< Length of the last transaction, in bits.
} spi_slave_hal_context_t;
typedef struct {
uint32_t host_id; ///< SPI controller ID
spi_dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address
spi_dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address
} spi_slave_hal_config_t;
/**
* Init the peripheral and the context.
*
* @param hal Context of the HAL layer.
* @param hal Context of the HAL layer.
* @param host_id Index of the SPI peripheral. 0 for SPI1, 1 for HSPI (SPI2) and 2 for VSPI (SPI3).
*/
void spi_slave_hal_init(spi_slave_hal_context_t *hal, int host_id);
void spi_slave_hal_init(spi_slave_hal_context_t *hal, const spi_slave_hal_config_t *hal_config);
/**
* Deinit the peripheral (and the context if needed).

View File

@@ -22,7 +22,7 @@
* The HAL layer for SPI Slave HD mode, currently only segment mode is supported
*
* Usage:
* - Firstly, initialize the slave with `slave_hd_hal_init`
* - Firstly, initialize the slave with `spi_slave_hd_hal_init`
*
* - Event handling:
* - (Optional) Call ``spi_slave_hd_hal_enable_event_intr`` to enable the used interrupts
@@ -59,51 +59,54 @@
/// Configuration of the HAL
typedef struct {
int host_id; ///< Host ID of the spi peripheral
int spics_io_num; ///< CS GPIO pin for this device
uint8_t mode; ///< SPI mode (0-3)
int command_bits; ///< command field bits, multiples of 8 and at least 8.
int address_bits; ///< address field bits, multiples of 8 and at least 8.
int dummy_bits; ///< dummy field bits, multiples of 8 and at least 8.
uint32_t host_id; ///< Host ID of the spi peripheral
spi_dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address
spi_dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address
uint32_t spics_io_num; ///< CS GPIO pin for this device
uint8_t mode; ///< SPI mode (0-3)
uint32_t command_bits; ///< command field bits, multiples of 8 and at least 8.
uint32_t address_bits; ///< address field bits, multiples of 8 and at least 8.
uint32_t dummy_bits; ///< dummy field bits, multiples of 8 and at least 8.
struct {
uint32_t tx_lsbfirst : 1;///< Whether TX data should be sent with LSB first.
uint32_t rx_lsbfirst : 1;///< Whether RX data should be read with LSB first.
uint32_t tx_lsbfirst : 1; ///< Whether TX data should be sent with LSB first.
uint32_t rx_lsbfirst : 1; ///< Whether RX data should be read with LSB first.
};
int dma_chan; ///< The dma channel used.
uint32_t dma_chan; ///< The dma channel used.
} spi_slave_hd_hal_config_t;
/// Context of the HAL, initialized by :cpp:func:`slave_hd_hal_init`.
/// Context of the HAL, initialized by :cpp:func:`spi_slave_hd_hal_init`.
typedef struct {
spi_dev_t* dev; ///< Beginning address of the peripheral registers.
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
spi_dev_t *dev; ///< Beginning address of the peripheral registers.
spi_dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
spi_dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
* The amount should be larger than dmadesc_n. The driver should ensure that
* the data to be sent is shorter than the descriptors can hold.
*/
/* Internal status used by the HAL implementation, initialized as 0. */
uint32_t intr_not_triggered;
uint32_t intr_not_triggered;
} spi_slave_hd_hal_context_t;
/**
* @brief Initialize the hardware and part of the context
*
* @param hal Context of the HAL layer
* @param config Configuration of the HAL
* @param hal Context of the HAL layer
* @param hal_config Configuration of the HAL
*/
void slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *config);
void spi_slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *hal_config);
/**
* @brief Check and clear signal of one event
*
* @param hal Context of the HAL layer
* @param ev Event to check
* @return true if event triggered, otherwise false
* @return True if event triggered, otherwise false
*/
bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev);
@@ -116,7 +119,7 @@ bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t* hal, spi_eve
*
* @param hal Context of the HAL layer
* @param ev Event to check and disable
* @return true if event triggered, otherwise false
* @return True if event triggered, otherwise false
*/
bool spi_slave_hd_hal_check_disable_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev);
@@ -156,7 +159,7 @@ void spi_slave_hd_hal_rxdma(spi_slave_hd_hal_context_t *hal, uint8_t *out_buf, s
* @brief Get the length of total received data
*
* @param hal Context of the HAL layer
* @return The received length
* @return The received length
*/
int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal);
@@ -167,8 +170,8 @@ int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal);
* @brief Start the TX DMA operation with the specified buffer
*
* @param hal Context of the HAL layer
* @param data Buffer of data to send
* @param len Size of the buffer, also the maximum length to send
* @param data Buffer of data to send
* @param len Size of the buffer, also the maximum length to send
*/
void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size_t len);
@@ -179,9 +182,9 @@ void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size
* @brief Read from the shared register buffer
*
* @param hal Context of the HAL layer
* @param addr Address of the shared regsiter to read
* @param out_data Buffer to store the read data
* @param len Length to read from the shared buffer
* @param addr Address of the shared regsiter to read
* @param out_data Buffer to store the read data
* @param len Length to read from the shared buffer
*/
void spi_slave_hd_hal_read_buffer(spi_slave_hd_hal_context_t *hal, int addr, uint8_t *out_data, size_t len);
@@ -199,7 +202,7 @@ void spi_slave_hd_hal_write_buffer(spi_slave_hd_hal_context_t *hal, int addr, ui
* @brief Get the length of previous transaction.
*
* @param hal Context of the HAL layer
* @return The length of previous transaction
* @return The length of previous transaction
*/
int spi_slave_hd_hal_get_rxlen(spi_slave_hd_hal_context_t *hal);
@@ -207,6 +210,6 @@ int spi_slave_hd_hal_get_rxlen(spi_slave_hd_hal_context_t *hal);
* @brief Get the address of last transaction
*
* @param hal Context of the HAL layer
* @return The address of last transaction
* @return The address of last transaction
*/
int spi_slave_hd_hal_get_last_addr(spi_slave_hd_hal_context_t *hal);

View File

@@ -24,12 +24,24 @@ static const char SPI_HAL_TAG[] = "spi_hal";
return (ret_val); \
}
void spi_hal_init(spi_hal_context_t *hal, int host_id)
static void s_spi_hal_dma_init_config(const spi_hal_context_t *hal)
{
spi_dma_ll_rx_enable_burst_data(hal->dma_in, 1);
spi_dma_ll_tx_enable_burst_data(hal->dma_out, 1);
spi_dma_ll_rx_enable_burst_desc(hal->dma_in, 1);
spi_dma_ll_tx_enable_burst_desc(hal->dma_out, 1);
}
void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_dma_config_t *dma_config)
{
memset(hal, 0, sizeof(spi_hal_context_t));
spi_dev_t *hw = spi_periph_signal[host_id].hw;
spi_dev_t *hw = SPI_LL_GET_HW(host_id);
hal->hw = hw;
hal->dma_in = dma_config->dma_in;
hal->dma_out = dma_config->dma_out;
spi_ll_master_init(hw);
s_spi_hal_dma_init_config(hal);
//Force a transaction done interrupt. This interrupt won't fire yet because
//we initialized the SPI interrupt as disabled. This way, we can just
@@ -38,6 +50,9 @@ void spi_hal_init(spi_hal_context_t *hal, int host_id)
spi_ll_enable_int(hw);
spi_ll_set_int_stat(hw);
spi_ll_set_mosi_delay(hw, 0, 0);
//Save the dma configuration in ``spi_hal_context_t``
memcpy(&hal->dma_config, dma_config, sizeof(spi_hal_dma_config_t));
}
void spi_hal_deinit(spi_hal_context_t *hal)
@@ -49,20 +64,20 @@ void spi_hal_deinit(spi_hal_context_t *hal)
}
}
esp_err_t spi_hal_cal_clock_conf(const spi_hal_context_t *hal, int speed_hz, int duty_cycle, bool use_gpio, int input_delay_ns, int *out_freq, spi_hal_timing_conf_t *timing_conf)
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf)
{
spi_hal_timing_conf_t temp_conf;
int eff_clk_n = spi_ll_master_cal_clock(APB_CLK_FREQ, speed_hz, duty_cycle, &temp_conf.clock_reg);
int eff_clk_n = spi_ll_master_cal_clock(APB_CLK_FREQ, timing_param->clock_speed_hz, timing_param->duty_cycle, &temp_conf.clock_reg);
//When the speed is too fast, we may need to use dummy cycles to compensate the reading.
//But these don't work for full-duplex connections.
spi_hal_cal_timing(eff_clk_n, use_gpio, input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay);
spi_hal_cal_timing(eff_clk_n, timing_param->use_gpio, timing_param->input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay);
#ifdef CONFIG_IDF_TARGET_ESP32
const int freq_limit = spi_hal_get_freq_limit(use_gpio, input_delay_ns);
const int freq_limit = spi_hal_get_freq_limit(timing_param->use_gpio, timing_param->input_delay_ns);
SPI_HAL_CHECK(hal->half_duplex || temp_conf.timing_dummy == 0 || hal->no_compensate,
SPI_HAL_CHECK(timing_param->half_duplex || temp_conf.timing_dummy == 0 || timing_param->no_compensate,
"When work in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\
Try to use IOMUX pins to increase the frequency limit, or use the half duplex mode.\n\
Please note the SPI master can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\

View File

@@ -17,29 +17,29 @@
#include "hal/spi_hal.h"
void spi_hal_setup_device(const spi_hal_context_t *hal)
void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev)
{
//Configure clock settings
spi_dev_t *hw = hal->hw;
#ifdef SOC_SPI_SUPPORT_AS_CS
spi_ll_master_set_cksel(hw, hal->cs_pin_id, hal->as_cs);
spi_ll_master_set_cksel(hw, dev->cs_pin_id, dev->as_cs);
#endif
spi_ll_master_set_pos_cs(hw, hal->cs_pin_id, hal->positive_cs);
spi_ll_master_set_clock_by_reg(hw, &hal->timing_conf->clock_reg);
spi_ll_master_set_pos_cs(hw, dev->cs_pin_id, dev->positive_cs);
spi_ll_master_set_clock_by_reg(hw, &dev->timing_conf.clock_reg);
//Configure bit order
spi_ll_set_rx_lsbfirst(hw, hal->rx_lsbfirst);
spi_ll_set_tx_lsbfirst(hw, hal->tx_lsbfirst);
spi_ll_master_set_mode(hw, hal->mode);
spi_ll_set_rx_lsbfirst(hw, dev->rx_lsbfirst);
spi_ll_set_tx_lsbfirst(hw, dev->tx_lsbfirst);
spi_ll_master_set_mode(hw, dev->mode);
//Configure misc stuff
spi_ll_set_half_duplex(hw, hal->half_duplex);
spi_ll_set_sio_mode(hw, hal->sio);
spi_ll_set_half_duplex(hw, dev->half_duplex);
spi_ll_set_sio_mode(hw, dev->sio);
//Configure CS pin and timing
spi_ll_master_set_cs_setup(hw, hal->cs_setup);
spi_ll_master_set_cs_hold(hw, hal->cs_hold);
spi_ll_master_select_cs(hw, hal->cs_pin_id);
spi_ll_master_set_cs_setup(hw, dev->cs_setup);
spi_ll_master_set_cs_hold(hw, dev->cs_hold);
spi_ll_master_select_cs(hw, dev->cs_pin_id);
}
void spi_hal_setup_trans(const spi_hal_context_t *hal)
void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans)
{
spi_dev_t *hw = hal->hw;
@@ -48,23 +48,23 @@ void spi_hal_setup_trans(const spi_hal_context_t *hal)
//We should be done with the transmission.
assert(spi_ll_get_running_cmd(hw) == 0);
spi_ll_master_set_io_mode(hw, hal->io_mode);
spi_ll_master_set_io_mode(hw, trans->io_mode);
int extra_dummy = 0;
//when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist
if (hal->rcv_buffer && !hal->no_compensate && hal->half_duplex) {
extra_dummy = hal->timing_conf->timing_dummy;
if (trans->rcv_buffer && !dev->no_compensate && dev->half_duplex) {
extra_dummy = dev->timing_conf.timing_dummy;
}
//SPI iface needs to be configured for a delay in some cases.
//configure dummy bits
spi_ll_set_dummy(hw, extra_dummy + hal->dummy_bits);
spi_ll_set_dummy(hw, extra_dummy + trans->dummy_bits);
uint32_t miso_delay_num = 0;
uint32_t miso_delay_mode = 0;
if (hal->timing_conf->timing_miso_delay < 0) {
if (dev->timing_conf.timing_miso_delay < 0) {
//if the data comes too late, delay half a SPI clock to improve reading
switch (hal->mode) {
switch (dev->mode) {
case 0:
miso_delay_mode = 2;
break;
@@ -81,24 +81,24 @@ void spi_hal_setup_trans(const spi_hal_context_t *hal)
miso_delay_num = 0;
} else {
//if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing
miso_delay_num = extra_dummy ? hal->timing_conf->timing_miso_delay : 0;
miso_delay_num = extra_dummy ? dev->timing_conf.timing_miso_delay : 0;
miso_delay_mode = 0;
}
spi_ll_set_miso_delay(hw, miso_delay_mode, miso_delay_num);
spi_ll_set_mosi_bitlen(hw, hal->tx_bitlen);
spi_ll_set_mosi_bitlen(hw, trans->tx_bitlen);
if (hal->half_duplex) {
spi_ll_set_miso_bitlen(hw, hal->rx_bitlen);
if (dev->half_duplex) {
spi_ll_set_miso_bitlen(hw, trans->rx_bitlen);
} else {
//rxlength is not used in full-duplex mode
spi_ll_set_miso_bitlen(hw, hal->tx_bitlen);
spi_ll_set_miso_bitlen(hw, trans->tx_bitlen);
}
//Configure bit sizes, load addr and command
int cmdlen = hal->cmd_bits;
int addrlen = hal->addr_bits;
if (!hal->half_duplex && hal->cs_setup != 0) {
int cmdlen = trans->cmd_bits;
int addrlen = trans->addr_bits;
if (!dev->half_duplex && dev->cs_setup != 0) {
/* The command and address phase is not compatible with cs_ena_pretrans
* in full duplex mode.
*/
@@ -109,45 +109,61 @@ void spi_hal_setup_trans(const spi_hal_context_t *hal)
spi_ll_set_addr_bitlen(hw, addrlen);
spi_ll_set_command_bitlen(hw, cmdlen);
spi_ll_set_command(hw, hal->cmd, cmdlen, hal->tx_lsbfirst);
spi_ll_set_address(hw, hal->addr, addrlen, hal->tx_lsbfirst);
spi_ll_set_command(hw, trans->cmd, cmdlen, dev->tx_lsbfirst);
spi_ll_set_address(hw, trans->addr, addrlen, dev->tx_lsbfirst);
//Save the transaction attributes for internal usage.
memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t));
}
void spi_hal_prepare_data(const spi_hal_context_t *hal)
void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans)
{
spi_dev_t *hw = hal->hw;
spi_ll_reset_dma(hw);
spi_ll_dma_fifo_reset(hal->hw);
//Fill DMA descriptors
if (hal->rcv_buffer) {
if (trans->rcv_buffer) {
if (!hal->dma_enabled) {
//No need to setup anything; we'll copy the result out of the work registers directly later.
} else {
lldesc_setup_link(hal->dmadesc_rx, hal->rcv_buffer, ((hal->rx_bitlen + 7) / 8), true);
spi_ll_rxdma_start(hw, hal->dmadesc_rx);
lldesc_setup_link(hal->dma_config.dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true);
spi_dma_ll_rx_reset(hal->dma_in);
spi_ll_dma_rx_enable(hal->hw, 1);
spi_dma_ll_rx_start(hal->dma_in, hal->dma_config.dmadesc_rx);
}
} else {
//DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon
if (hal->dma_enabled) {
spi_ll_rxdma_start(hw, 0);
spi_ll_dma_rx_enable(hal->hw, 1);
spi_dma_ll_rx_start(hal->dma_in, 0);
}
}
if (hal->send_buffer) {
if (trans->send_buffer) {
if (!hal->dma_enabled) {
//Need to copy data to registers manually
spi_ll_write_buffer(hw, hal->send_buffer, hal->tx_bitlen);
spi_ll_write_buffer(hw, trans->send_buffer, trans->tx_bitlen);
} else {
lldesc_setup_link(hal->dmadesc_tx, hal->send_buffer, (hal->tx_bitlen + 7) / 8, false);
spi_ll_txdma_start(hw, hal->dmadesc_tx);
lldesc_setup_link(hal->dma_config.dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false);
spi_dma_ll_tx_reset(hal->dma_out);
spi_ll_dma_tx_enable(hal->hw, 1);
spi_dma_ll_tx_start(hal->dma_out, hal->dma_config.dmadesc_tx);
}
}
//in ESP32 these registers should be configured after the DMA is set
if ((!hal->half_duplex && hal->rcv_buffer) || hal->send_buffer) {
if ((!dev->half_duplex && trans->rcv_buffer) || trans->send_buffer) {
spi_ll_enable_mosi(hw, 1);
} else {
spi_ll_enable_mosi(hw, 0);
}
spi_ll_enable_miso(hw, (hal->rcv_buffer) ? 1 : 0);
spi_ll_enable_miso(hw, (trans->rcv_buffer) ? 1 : 0);
}
void spi_hal_user_start(const spi_hal_context_t *hal)
@@ -162,8 +178,10 @@ bool spi_hal_usr_is_done(const spi_hal_context_t *hal)
void spi_hal_fetch_result(const spi_hal_context_t *hal)
{
if (hal->rcv_buffer && !hal->dma_enabled) {
const spi_hal_trans_config_t *trans = &hal->trans_config;
if (trans->rcv_buffer && !hal->dma_enabled) {
//Need to copy from SPI regs to result buffer.
spi_ll_read_buffer(hal->hw, hal->rcv_buffer, hal->rx_bitlen);
spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen);
}
}

View File

@@ -1,19 +1,30 @@
#include "hal/spi_slave_hal.h"
#include "hal/spi_ll.h"
void spi_slave_hal_init(spi_slave_hal_context_t *hal, int host_id)
static void s_spi_slave_hal_dma_init_config(const spi_slave_hal_context_t *hal)
{
spi_dma_ll_rx_enable_burst_data(hal->dma_in, 1);
spi_dma_ll_tx_enable_burst_data(hal->dma_out, 1);
spi_dma_ll_rx_enable_burst_desc(hal->dma_in, 1);
spi_dma_ll_tx_enable_burst_desc(hal->dma_out, 1);
}
void spi_slave_hal_init(spi_slave_hal_context_t *hal, const spi_slave_hal_config_t *hal_config)
{
memset(hal, 0, sizeof(spi_slave_hal_context_t));
spi_dev_t *hw = spi_periph_signal[host_id].hw;
spi_dev_t *hw = SPI_LL_GET_HW(hal_config->host_id);
hal->hw = hw;
hal->dma_in = hal_config->dma_in;
hal->dma_out = hal_config->dma_out;
s_spi_slave_hal_dma_init_config(hal);
spi_ll_slave_init(hal->hw);
//Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
//disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling
//any transactions that are queued.
spi_ll_set_int_stat(hal->hw);
spi_ll_slave_set_int_type(hal->hw, SPI_LL_INT_TYPE_NORMAL);
spi_ll_enable_int(hal->hw);
}
void spi_slave_hal_setup_device(const spi_slave_hal_context_t *hal)

View File

@@ -14,28 +14,43 @@ void spi_slave_hal_user_start(const spi_slave_hal_context_t *hal)
void spi_slave_hal_prepare_data(const spi_slave_hal_context_t *hal)
{
spi_ll_slave_reset(hal->hw);
if (hal->use_dma) {
spi_ll_reset_dma(hal->hw);
spi_ll_dma_fifo_reset(hal->hw);
//Fill DMA descriptors
if (hal->rx_buffer) {
if (hal->rx_buffer) {
lldesc_setup_link(hal->dmadesc_rx, hal->rx_buffer, ((hal->bitlen + 7) / 8), true);
spi_ll_rxdma_start(hal->hw, &hal->dmadesc_rx[0]);
//reset dma inlink, this should be reset before spi related reset
spi_dma_ll_rx_reset(hal->dma_in);
spi_ll_slave_reset(hal->hw);
spi_ll_infifo_full_clr(hal->hw);
spi_ll_dma_rx_enable(hal->hw, 1);
spi_dma_ll_rx_start(hal->dma_in, &hal->dmadesc_rx[0]);
}
if (hal->tx_buffer) {
lldesc_setup_link(hal->dmadesc_tx, hal->tx_buffer, (hal->bitlen + 7) / 8, false);
spi_ll_txdma_start(hal->hw, (&hal->dmadesc_tx[0]));
//reset dma outlink, this should be reset before spi related reset
spi_dma_ll_tx_reset(hal->dma_out);
spi_ll_slave_reset(hal->hw);
spi_ll_outfifo_empty_clr(hal->hw);
spi_ll_dma_tx_enable(hal->hw, 1);
spi_dma_ll_tx_start(hal->dma_out, (&hal->dmadesc_tx[0]));
}
} else {
//No DMA. Turn off SPI and copy data to transmit buffers.
if (hal->tx_buffer) {
spi_ll_slave_reset(hal->hw);
spi_ll_write_buffer(hal->hw, hal->tx_buffer, hal->bitlen);
}
}
spi_ll_slave_set_rx_bitlen(hal->hw, hal->bitlen);
spi_ll_slave_set_tx_bitlen(hal->hw, hal->bitlen);
spi_ll_enable_mosi(hal->hw, (hal->tx_buffer == NULL) ? 0 : 1);
spi_ll_enable_miso(hal->hw, (hal->rx_buffer == NULL) ? 0 : 1);
}
@@ -53,7 +68,6 @@ void spi_slave_hal_store_result(spi_slave_hal_context_t *hal)
//Copy result out
spi_ll_read_buffer(hal->hw, hal->rx_buffer, hal->bitlen);
}
spi_ll_slave_set_int_type(hal->hw, SPI_LL_INT_TYPE_NORMAL);
}
uint32_t spi_slave_hal_get_rcv_bitlen(spi_slave_hal_context_t *hal)

View File

@@ -19,27 +19,36 @@
#include "esp_attr.h"
#include "esp_err.h"
#include "sdkconfig.h"
#include "soc/spi_periph.h"
#include "soc/lldesc.h"
#include "hal/spi_slave_hd_hal.h"
static void s_spi_slave_hd_hal_dma_init_config(const spi_slave_hd_hal_context_t *hal)
{
spi_dma_ll_rx_enable_burst_data(hal->dma_in, 1);
spi_dma_ll_tx_enable_burst_data(hal->dma_out, 1);
spi_dma_ll_rx_enable_burst_desc(hal->dma_in, 1);
spi_dma_ll_tx_enable_burst_desc(hal->dma_out, 1);
}
void slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *config)
void spi_slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *hal_config)
{
memset(hal, 0, sizeof(spi_slave_hd_hal_context_t));
spi_dev_t* hw = SPI_LL_GET_HW(config->host_id);
spi_dev_t* hw = SPI_LL_GET_HW(hal_config->host_id);
hal->dev = hw;
hal->dma_in = hal_config->dma_in;
hal->dma_out = hal_config->dma_out;
//Configure slave
s_spi_slave_hd_hal_dma_init_config(hal);
spi_ll_slave_hd_init(hw);
spi_ll_set_addr_bitlen(hw, config->address_bits);
spi_ll_set_command_bitlen(hw, config->command_bits);
spi_ll_set_dummy(hw, config->dummy_bits);
spi_ll_set_rx_lsbfirst(hw, config->rx_lsbfirst);
spi_ll_set_tx_lsbfirst(hw, config->tx_lsbfirst);
spi_ll_slave_set_mode(hw, config->mode, (config->dma_chan != 0));
spi_ll_set_addr_bitlen(hw, hal_config->address_bits);
spi_ll_set_command_bitlen(hw, hal_config->command_bits);
spi_ll_set_dummy(hw, hal_config->dummy_bits);
spi_ll_set_rx_lsbfirst(hw, hal_config->rx_lsbfirst);
spi_ll_set_tx_lsbfirst(hw, hal_config->tx_lsbfirst);
spi_ll_slave_set_mode(hw, hal_config->mode, (hal_config->dma_chan != 0));
spi_ll_disable_intr(hw, UINT32_MAX);
spi_ll_clear_intr(hw, UINT32_MAX);
@@ -66,25 +75,31 @@ void slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_c
SPI_LL_TRANS_LEN_COND_RDBUF |
SPI_LL_TRANS_LEN_COND_RDDMA);
spi_ll_slave_set_seg_mode(hw, true);
spi_ll_slave_set_seg_mode(hal->dev, true);
}
void spi_slave_hd_hal_rxdma(spi_slave_hd_hal_context_t *hal, uint8_t *out_buf, size_t len)
{
lldesc_setup_link(hal->dmadesc_rx, out_buf, len, true);
spi_ll_rxdma_reset(hal->dev);
spi_dma_ll_rx_reset(hal->dma_in);
spi_ll_infifo_full_clr(hal->dev);
spi_ll_clear_intr(hal->dev, SPI_LL_INTR_WR_DONE);
spi_ll_rxdma_start(hal->dev, &hal->dmadesc_rx[0]);
spi_ll_dma_rx_enable(hal->dev, 1);
spi_dma_ll_rx_start(hal->dma_in, &hal->dmadesc_rx[0]);
}
void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size_t len)
{
lldesc_setup_link(hal->dmadesc_tx, data, len, false);
spi_ll_txdma_reset(hal->dev);
spi_dma_ll_tx_reset(hal->dma_out);
spi_ll_outfifo_empty_clr(hal->dev);
spi_ll_clear_intr(hal->dev, SPI_LL_INTR_CMD8);
spi_ll_txdma_start(hal->dev, &hal->dmadesc_tx[0]);
spi_ll_dma_tx_enable(hal->dev, 1);
spi_dma_ll_tx_start(hal->dma_out, &hal->dmadesc_tx[0]);
}
static spi_ll_intr_t get_event_intr(spi_event_t ev)
@@ -179,4 +194,4 @@ int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal)
{
lldesc_t* desc = &hal->dmadesc_rx[0];
return lldesc_get_received_len(desc, NULL);
}
}