mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-03 20:54:32 +02:00
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:
@@ -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;
|
||||
|
||||
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;
|
||||
//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;
|
||||
|
||||
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);
|
||||
|
||||
int freq;
|
||||
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;
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
@@ -38,9 +38,24 @@
|
||||
#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 {
|
||||
@@ -50,12 +65,12 @@ typedef struct {
|
||||
} spi_hal_timing_conf_t;
|
||||
|
||||
/**
|
||||
* Context that should be maintained by both the driver and the HAL.
|
||||
* DMA configuration structure
|
||||
* Should be set by driver at initialization
|
||||
*/
|
||||
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 */
|
||||
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.
|
||||
@@ -65,36 +80,13 @@ typedef struct {
|
||||
* 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``.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
#ifdef SOC_SPI_SUPPORT_AS_CS
|
||||
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
|
||||
};//boolean configurations
|
||||
} spi_hal_dma_config_t;
|
||||
|
||||
/*
|
||||
* Transaction specific (data), all these parameters will be updated to the
|
||||
* peripheral every transaction.
|
||||
/**
|
||||
* 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
|
||||
@@ -105,16 +97,56 @@ typedef struct {
|
||||
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.
|
||||
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; /**< 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 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
|
||||
#endif
|
||||
uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific
|
||||
};//boolean configurations
|
||||
} spi_hal_dev_config_t;
|
||||
|
||||
/**
|
||||
* Init the peripheral and the context.
|
||||
*
|
||||
* @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).
|
||||
@@ -127,22 +159,27 @@ 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_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_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_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.
|
||||
@@ -173,19 +210,13 @@ 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 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.
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#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.
|
||||
@@ -43,6 +44,8 @@
|
||||
typedef struct {
|
||||
/* 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.
|
||||
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
|
||||
@@ -77,13 +80,19 @@ typedef struct {
|
||||
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 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).
|
||||
|
@@ -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,23 +59,27 @@
|
||||
|
||||
/// 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
|
||||
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)
|
||||
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 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.
|
||||
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.
|
||||
@@ -89,21 +93,20 @@ typedef struct {
|
||||
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_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);
|
||||
|
||||
|
@@ -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\
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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) {
|
||||
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)
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user