12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007 |
- #include <string.h>
- #include "driver/spi_common_internal.h"
- #include "driver/spi_master.h"
- #include "esp_log.h"
- #include "freertos/task.h"
- #include "freertos/queue.h"
- #include "freertos/semphr.h"
- #include "soc/soc_memory_layout.h"
- #include "driver/gpio.h"
- #include "hal/spi_hal.h"
- #include "esp_heap_caps.h"
- typedef struct spi_device_t spi_device_t;
- typedef struct {
- spi_transaction_t *trans;
- const uint32_t *buffer_to_send;
-
- uint32_t *buffer_to_rcv;
- } spi_trans_priv_t;
- typedef struct {
- int id;
- spi_device_t* device[DEV_NUM_MAX];
- intr_handle_t intr;
- spi_hal_context_t hal;
- spi_trans_priv_t cur_trans_buf;
- int cur_cs;
- const spi_bus_attr_t* bus_attr;
-
- spi_device_t* device_acquiring_lock;
- bool polling;
-
- SemaphoreHandle_t mutex;
- int count;
- } spi_host_t;
- struct spi_device_t {
- int id;
- QueueHandle_t trans_queue;
- QueueHandle_t ret_queue;
- spi_device_interface_config_t cfg;
- spi_hal_dev_config_t hal_dev;
- spi_host_t *host;
- spi_bus_lock_dev_handle_t dev_lock;
- };
- static spi_host_t* bus_driver_ctx[SOC_SPI_PERIPH_NUM] = {};
- static const char *SPI_TAG = "spi_master";
- #define SPI_CHECK(a, str, ret_val, ...) \
- if (unlikely(!(a))) { \
- ESP_LOGE(SPI_TAG,"%s(%d): "str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
- return (ret_val); \
- }
- static void spi_intr(void *arg);
- static void spi_bus_intr_enable(void *host);
- static void spi_bus_intr_disable(void *host);
- static esp_err_t spi_master_deinit_driver(void* arg);
- static inline bool is_valid_host(spi_host_device_t host)
- {
- #if CONFIG_IDF_TARGET_ESP32
- return host >= SPI1_HOST && host <= SPI3_HOST;
- #elif (SOC_SPI_PERIPH_NUM == 2)
- return host == SPI2_HOST;
- #elif (SOC_SPI_PERIPH_NUM == 3)
- return host >= SPI2_HOST && host <= SPI3_HOST;
- #endif
- }
- static esp_err_t spi_master_init_driver(spi_host_device_t host_id)
- {
- esp_err_t err = ESP_OK;
- const spi_bus_attr_t* bus_attr = spi_bus_get_attr(host_id);
- SPI_CHECK(bus_attr != NULL, "host_id not initialized", ESP_ERR_INVALID_STATE);
- SPI_CHECK(bus_attr->lock != NULL, "SPI Master cannot attach to bus. (Check CONFIG_SPI_FLASH_SHARE_SPI1_BUS)", ESP_ERR_INVALID_ARG);
-
- spi_host_t* host = heap_caps_malloc(sizeof(spi_host_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
- if (host == NULL) {
- err = ESP_ERR_NO_MEM;
- goto cleanup;
- }
- *host = (spi_host_t) {
- .id = host_id,
- .cur_cs = DEV_NUM_MAX,
- .polling = false,
- .device_acquiring_lock = NULL,
- .bus_attr = bus_attr,
- };
- if (host_id != SPI1_HOST) {
-
- err = esp_intr_alloc(spicommon_irqsource_for_host(host_id),
- bus_attr->bus_cfg.intr_flags | ESP_INTR_FLAG_INTRDISABLED,
- spi_intr, host, &host->intr);
- if (err != ESP_OK) {
- goto cleanup;
- }
- }
-
- spi_hal_config_t hal_config = {
-
- .dma_in = SPI_LL_GET_HW(host_id),
- .dma_out = SPI_LL_GET_HW(host_id),
- .dma_enabled = bus_attr->dma_enabled,
- .dmadesc_tx = bus_attr->dmadesc_tx,
- .dmadesc_rx = bus_attr->dmadesc_rx,
- .tx_dma_chan = bus_attr->tx_dma_chan,
- .rx_dma_chan = bus_attr->rx_dma_chan,
- .dmadesc_n = bus_attr->dma_desc_num,
- };
- spi_hal_init(&host->hal, host_id, &hal_config);
- if (host_id != SPI1_HOST) {
-
- spi_bus_lock_handle_t lock = spi_bus_lock_get_by_id(host_id);
- spi_bus_lock_set_bg_control(lock, spi_bus_intr_enable, spi_bus_intr_disable, host);
- spi_bus_register_destroy_func(host_id, spi_master_deinit_driver, host);
- }
- bus_driver_ctx[host_id] = host;
- return ESP_OK;
- cleanup:
- if (host) {
- spi_hal_deinit(&host->hal);
- if (host->intr) {
- esp_intr_free(host->intr);
- }
- }
- free(host);
- return err;
- }
- static esp_err_t spi_master_deinit_driver(void* arg)
- {
- spi_host_t *host = (spi_host_t*)arg;
- SPI_CHECK(host != NULL, "host_id not in use", ESP_ERR_INVALID_STATE);
- int host_id = host->id;
- SPI_CHECK(is_valid_host(host_id), "invalid host_id", ESP_ERR_INVALID_ARG);
- int x;
- for (x=0; x<DEV_NUM_MAX; x++) {
- SPI_CHECK(host->device[x] == NULL, "not all CSses freed", ESP_ERR_INVALID_STATE);
- }
- spi_hal_deinit(&host->hal);
- if (host->intr) {
- esp_intr_free(host->intr);
- }
- free(host);
- bus_driver_ctx[host_id] = NULL;
- return ESP_OK;
- }
- void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dummy_o, int* cycles_remain_o)
- {
- int timing_dummy;
- int timing_miso_delay;
- spi_hal_cal_timing(eff_clk, gpio_is_used, input_delay_ns, &timing_dummy, &timing_miso_delay);
- if (dummy_o) *dummy_o = timing_dummy;
- if (cycles_remain_o) *cycles_remain_o = timing_miso_delay;
- }
- 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);
- }
- 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)
- {
- spi_device_t *dev = NULL;
- esp_err_t err = ESP_OK;
- SPI_CHECK(is_valid_host(host_id), "invalid host", ESP_ERR_INVALID_ARG);
- if (bus_driver_ctx[host_id] == NULL) {
-
- err = spi_master_init_driver(host_id);
- if (err != ESP_OK) {
- return err;
- }
- }
- spi_host_t *host = bus_driver_ctx[host_id];
- const spi_bus_attr_t* bus_attr = host->bus_attr;
- SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG);
- SPI_CHECK(dev_config->clock_speed_hz > 0, "invalid sclk speed", ESP_ERR_INVALID_ARG);
- #ifdef CONFIG_IDF_TARGET_ESP32
-
-
- SPI_CHECK(dev_config->cs_ena_pretrans <= 1 || (dev_config->address_bits == 0 && dev_config->command_bits == 0) ||
- (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "In full-duplex mode, only support cs pretrans delay = 1 and without address_bits and command_bits", ESP_ERR_INVALID_ARG);
- #endif
- uint32_t lock_flag = ((dev_config->spics_io_num != -1)? SPI_BUS_LOCK_DEV_FLAG_CS_REQUIRED: 0);
- spi_bus_lock_dev_config_t lock_config = {
- .flags = lock_flag,
- };
- spi_bus_lock_dev_handle_t dev_handle;
- err = spi_bus_lock_register_dev(bus_attr->lock, &lock_config, &dev_handle);
- if (err != ESP_OK) {
- goto nomem;
- }
- 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);
-
- 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
- };
-
- spi_hal_timing_conf_t 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);
-
- dev = malloc(sizeof(spi_device_t));
- if (dev == NULL) goto nomem;
- memset(dev, 0, sizeof(spi_device_t));
- dev->id = freecs;
- dev->dev_lock = dev_handle;
-
- dev->trans_queue = xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv_t));
- dev->ret_queue = xQueueCreate(dev_config->queue_size, sizeof(spi_trans_priv_t));
- if (!dev->trans_queue || !dev->ret_queue) {
- goto nomem;
- }
-
- memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
- dev->cfg.duty_cycle_pos = duty_cycle;
-
-
- if (dev_config->spics_io_num >= 0) {
- spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, use_gpio);
- }
-
-
- if (host->count++) {
- ESP_LOGI(SPI_TAG, "More than one device on SPI %d => creating mutex", host_id);
- host->mutex = xSemaphoreCreateMutex();
- }
-
- host->device[freecs] = dev;
-
- dev->host= host;
-
- 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;
-
-
- 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;
- #if 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;
- nomem:
- if (dev) {
- if (dev->trans_queue) vQueueDelete(dev->trans_queue);
- if (dev->ret_queue) vQueueDelete(dev->ret_queue);
- spi_bus_lock_unregister_dev(dev->dev_lock);
- }
- free(dev);
- return ESP_ERR_NO_MEM;
- }
- esp_err_t spi_bus_remove_device(spi_device_handle_t handle)
- {
- SPI_CHECK(handle!=NULL, "invalid handle", ESP_ERR_INVALID_ARG);
-
-
- SPI_CHECK(uxQueueMessagesWaiting(handle->trans_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE);
- SPI_CHECK(handle->host->cur_cs == DEV_NUM_MAX || handle->host->device[handle->host->cur_cs] != handle, "Have unfinished transactions", ESP_ERR_INVALID_STATE);
- SPI_CHECK(uxQueueMessagesWaiting(handle->ret_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE);
-
- int spics_io_num = handle->cfg.spics_io_num;
- if (spics_io_num >= 0) spicommon_cs_free_io(spics_io_num);
-
- vQueueDelete(handle->trans_queue);
- vQueueDelete(handle->ret_queue);
- spi_bus_lock_unregister_dev(handle->dev_lock);
- assert(handle->host->device[handle->id] == handle);
- handle->host->device[handle->id] = NULL;
- free(handle);
- return ESP_OK;
- }
- int spi_cal_clock(int fapb, int hz, int duty_cycle, uint32_t *reg_o)
- {
- return spi_ll_master_cal_clock(fapb, hz, duty_cycle, reg_o);
- }
- int spi_get_actual_clock(int fapb, int hz, int duty_cycle)
- {
- return spi_hal_master_cal_clock(fapb, hz, duty_cycle);
- }
- static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev)
- {
- spi_bus_lock_dev_handle_t dev_lock = dev->dev_lock;
- if (!spi_bus_lock_touch(dev_lock)) {
-
- return;
- }
- 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)
- {
- spi_bus_lock_dev_handle_t dev_lock = spi_bus_lock_get_acquiring_dev(host->bus_attr->lock);
- if (!dev_lock) return NULL;
- return host->device[spi_bus_lock_get_dev_id(dev_lock)];
- }
- static inline SPI_MASTER_ISR_ATTR bool spi_bus_device_is_polling(spi_device_t *dev)
- {
- return get_acquiring_dev(dev->host) == dev && dev->host->polling;
- }
- static void SPI_MASTER_ISR_ATTR spi_bus_intr_enable(void *host)
- {
- esp_intr_enable(((spi_host_t*)host)->intr);
- }
- static void SPI_MASTER_ISR_ATTR spi_bus_intr_disable(void *host)
- {
- esp_intr_disable(((spi_host_t*)host)->intr);
- }
- 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;
- 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;
-
- spi_setup_device(dev);
-
- 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;
-
- 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
- ));
- if (trans->flags & SPI_TRANS_VARIABLE_CMD) {
- hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits;
- } else {
- hal_trans.cmd_bits = dev->cfg.command_bits;
- }
- if (trans->flags & SPI_TRANS_VARIABLE_ADDR) {
- hal_trans.addr_bits = ((spi_transaction_ext_t *)trans)->address_bits;
- } else {
- hal_trans.addr_bits = dev->cfg.address_bits;
- }
- if (trans->flags & SPI_TRANS_VARIABLE_DUMMY) {
- hal_trans.dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits;
- } else {
- hal_trans.dummy_bits = dev->cfg.dummy_bits;
- }
- spi_hal_setup_trans(hal, hal_dev, &hal_trans);
- spi_hal_prepare_data(hal, hal_dev, &hal_trans);
-
- if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans);
-
- spi_hal_user_start(hal);
- }
- 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);
-
- spi_device_t* dev = host->device[host->cur_cs];
- if (dev->cfg.post_cb) dev->cfg.post_cb(cur_trans);
- host->cur_cs = DEV_NUM_MAX;
- }
- static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
- {
- BaseType_t do_yield = pdFALSE;
- spi_host_t *host = (spi_host_t *)arg;
- const spi_bus_attr_t* bus_attr = host->bus_attr;
- assert(spi_hal_usr_is_done(&host->hal));
-
- if (!spi_bus_lock_bg_entry(bus_attr->lock)) {
-
- assert(host->cur_cs != DEV_NUM_MAX);
-
- const int cs = host->cur_cs;
-
- if (bus_attr->dma_enabled) {
-
- spicommon_dmaworkaround_idle(bus_attr->tx_dma_chan);
- }
-
- spi_post_trans(host);
-
-
- xQueueSendFromISR(host->device[cs]->ret_queue, &host->cur_trans_buf, &do_yield);
- #ifdef CONFIG_PM_ENABLE
-
- esp_pm_lock_release(bus_attr->pm_lock);
- #endif
- }
-
- assert(host->cur_cs == DEV_NUM_MAX);
- spi_bus_lock_handle_t lock = host->bus_attr->lock;
- BaseType_t trans_found = pdFALSE;
-
- BUS_LOCK_DEBUG_EXECUTE_CHECK(spi_bus_lock_bg_req_exist(lock));
- do {
- spi_bus_lock_dev_handle_t acq_dev_lock = spi_bus_lock_get_acquiring_dev(lock);
- spi_bus_lock_dev_handle_t desired_dev = acq_dev_lock;
- bool resume_task = false;
- spi_device_t* device_to_send = NULL;
- if (!acq_dev_lock) {
-
-
-
-
- resume_task = spi_bus_lock_bg_check_dev_acq(lock, &desired_dev);
- }
- if (!resume_task) {
- bool dev_has_req = spi_bus_lock_bg_check_dev_req(desired_dev);
- if (dev_has_req) {
- device_to_send = host->device[spi_bus_lock_get_dev_id(desired_dev)];
- trans_found = xQueueReceiveFromISR(device_to_send->trans_queue, &host->cur_trans_buf, &do_yield);
- if (!trans_found) {
- spi_bus_lock_bg_clear_req(desired_dev);
- }
- }
- }
- if (trans_found) {
- spi_trans_priv_t *const cur_trans_buf = &host->cur_trans_buf;
- if (bus_attr->dma_enabled && (cur_trans_buf->buffer_to_rcv || cur_trans_buf->buffer_to_send)) {
-
-
- spicommon_dmaworkaround_transfer_active(bus_attr->tx_dma_chan);
- }
- spi_new_trans(device_to_send, cur_trans_buf);
- }
-
-
- } while (!spi_bus_lock_bg_exit(lock, trans_found, &do_yield));
- if (do_yield) portYIELD_FROM_ISR();
- }
- static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handle, spi_transaction_t *trans_desc)
- {
- SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG);
- spi_host_t *host = handle->host;
- const spi_bus_attr_t* bus_attr = host->bus_attr;
- bool tx_enabled = (trans_desc->flags & SPI_TRANS_USE_TXDATA) || (trans_desc->tx_buffer);
- 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->hal_dev.timing_conf.timing_dummy;
- bool is_half_duplex = ((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) != 0);
-
- SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 || trans_desc->rxlength <= 32, "SPI_TRANS_USE_RXDATA only available for rxdata transfer <= 32 bits", ESP_ERR_INVALID_ARG);
- SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 || trans_desc->length <= 32, "SPI_TRANS_USE_TXDATA only available for txdata transfer <= 32 bits", ESP_ERR_INVALID_ARG);
- SPI_CHECK(trans_desc->length <= bus_attr->max_transfer_sz*8, "txdata transfer > host maximum", ESP_ERR_INVALID_ARG);
- SPI_CHECK(trans_desc->rxlength <= bus_attr->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG);
- SPI_CHECK(is_half_duplex || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG);
-
- SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG);
- SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "incompatible iface params", ESP_ERR_INVALID_ARG);
- #ifdef CONFIG_IDF_TARGET_ESP32
- SPI_CHECK(!is_half_duplex || !bus_attr->dma_enabled || !rx_enabled || !tx_enabled, "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG );
- #elif CONFIG_IDF_TARGET_ESP32S3
- SPI_CHECK(!is_half_duplex || !tx_enabled || !rx_enabled, "SPI half duplex mode is not supported when both MOSI and MISO phases are enabled.", ESP_ERR_INVALID_ARG);
- #endif
-
- SPI_CHECK(trans_desc->length != 0 || !tx_enabled, "trans tx_buffer should be NULL and SPI_TRANS_USE_TXDATA should be cleared to skip MOSI phase.", ESP_ERR_INVALID_ARG);
-
-
- SPI_CHECK(!is_half_duplex || trans_desc->rxlength != 0 || !rx_enabled, "trans rx_buffer should be NULL and SPI_TRANS_USE_RXDATA should be cleared to skip MISO phase.", ESP_ERR_INVALID_ARG);
-
-
- if (trans_desc->rxlength==0 && !is_half_duplex) {
- trans_desc->rxlength=trans_desc->length;
- }
-
- SPI_CHECK(!tx_enabled || !rx_enabled || !dummy_enabled || !extra_dummy_enabled, "Dummy phase is not available when both data out and in are enabled", ESP_ERR_INVALID_ARG);
- return ESP_OK;
- }
- static SPI_MASTER_ISR_ATTR void uninstall_priv_desc(spi_trans_priv_t* trans_buf)
- {
- spi_transaction_t *trans_desc = trans_buf->trans;
- if ((void *)trans_buf->buffer_to_send != &trans_desc->tx_data[0] &&
- trans_buf->buffer_to_send != trans_desc->tx_buffer) {
- free((void *)trans_buf->buffer_to_send);
- }
-
- if ((void *)trans_buf->buffer_to_rcv != &trans_desc->rx_data[0] &&
- trans_buf->buffer_to_rcv != trans_desc->rx_buffer) {
- if (trans_desc->flags & SPI_TRANS_USE_RXDATA) {
- memcpy((uint8_t *) & trans_desc->rx_data[0], trans_buf->buffer_to_rcv, (trans_desc->rxlength + 7) / 8);
- } else {
- memcpy(trans_desc->rx_buffer, trans_buf->buffer_to_rcv, (trans_desc->rxlength + 7) / 8);
- }
- free(trans_buf->buffer_to_rcv);
- }
- }
- static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_transaction_t *trans_desc, spi_trans_priv_t* new_desc, bool isdma)
- {
- *new_desc = (spi_trans_priv_t) { .trans = trans_desc, };
-
- uint32_t* rcv_ptr;
- if ( trans_desc->flags & SPI_TRANS_USE_RXDATA ) {
- rcv_ptr = (uint32_t *)&trans_desc->rx_data[0];
- } else {
-
- rcv_ptr = trans_desc->rx_buffer;
- }
- if (rcv_ptr && isdma && (!esp_ptr_dma_capable(rcv_ptr) || ((int)rcv_ptr % 4 != 0))) {
-
- ESP_LOGD(SPI_TAG, "Allocate RX buffer for DMA" );
- rcv_ptr = heap_caps_malloc((trans_desc->rxlength + 31) / 8, MALLOC_CAP_DMA);
- if (rcv_ptr == NULL) goto clean_up;
- }
- new_desc->buffer_to_rcv = rcv_ptr;
-
- const uint32_t *send_ptr;
- if ( trans_desc->flags & SPI_TRANS_USE_TXDATA ) {
- send_ptr = (uint32_t *)&trans_desc->tx_data[0];
- } else {
-
- send_ptr = trans_desc->tx_buffer ;
- }
- if (send_ptr && isdma && !esp_ptr_dma_capable( send_ptr )) {
-
- ESP_LOGD(SPI_TAG, "Allocate TX buffer for DMA" );
- uint32_t *temp = heap_caps_malloc((trans_desc->length + 7) / 8, MALLOC_CAP_DMA);
- if (temp == NULL) goto clean_up;
- memcpy( temp, send_ptr, (trans_desc->length + 7) / 8 );
- send_ptr = temp;
- }
- new_desc->buffer_to_send = send_ptr;
- return ESP_OK;
- clean_up:
- uninstall_priv_desc(new_desc);
- 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);
- if (ret != ESP_OK) return ret;
- spi_host_t *host = handle->host;
- SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot queue new transaction while previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE );
- spi_trans_priv_t trans_buf;
- ret = setup_priv_desc(trans_desc, &trans_buf, (host->bus_attr->dma_enabled));
- if (ret != ESP_OK) return ret;
- #ifdef CONFIG_PM_ENABLE
- esp_pm_lock_acquire(host->bus_attr->pm_lock);
- #endif
-
- BaseType_t r = xQueueSend(handle->trans_queue, (void *)&trans_buf, ticks_to_wait);
- if (!r) {
- ret = ESP_ERR_TIMEOUT;
- #ifdef CONFIG_PM_ENABLE
-
- esp_pm_lock_release(host->bus_attr->pm_lock);
- #endif
- goto clean_up;
- }
-
- ret = spi_bus_lock_bg_request(handle->dev_lock);
- if (ret != ESP_OK) {
- goto clean_up;
- }
- return ESP_OK;
- clean_up:
- uninstall_priv_desc(&trans_buf);
- return ret;
- }
- esp_err_t SPI_MASTER_ATTR spi_device_get_trans_result(spi_device_handle_t handle, spi_transaction_t **trans_desc, TickType_t ticks_to_wait)
- {
- BaseType_t r;
- spi_trans_priv_t trans_buf;
- SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG);
-
- r=xQueueReceive(handle->ret_queue, (void*)&trans_buf, ticks_to_wait);
- if (!r) {
-
-
-
- return ESP_ERR_TIMEOUT;
- }
-
- uninstall_priv_desc(&trans_buf);
- (*trans_desc) = trans_buf.trans;
- return ESP_OK;
- }
- esp_err_t SPI_MASTER_ATTR spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc)
- {
- esp_err_t ret;
- spi_transaction_t *ret_trans;
-
- ret = spi_device_queue_trans(handle, trans_desc, portMAX_DELAY);
- if (ret != ESP_OK) return ret;
- ret = spi_device_get_trans_result(handle, &ret_trans, portMAX_DELAY);
- if (ret != ESP_OK) return ret;
- assert(ret_trans == trans_desc);
- 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;
- SPI_CHECK(wait==portMAX_DELAY, "acquire finite time not supported now.", ESP_ERR_INVALID_ARG);
- SPI_CHECK(!spi_bus_device_is_polling(device), "Cannot acquire bus when a polling transaction is in progress.", ESP_ERR_INVALID_STATE );
- esp_err_t ret = spi_bus_lock_acquire_start(device->dev_lock, wait);
- if (ret != ESP_OK) {
- return ret;
- }
- host->device_acquiring_lock = device;
- ESP_LOGD(SPI_TAG, "device%d locked the bus", device->id);
- #ifdef CONFIG_PM_ENABLE
-
-
- esp_pm_lock_acquire(host->bus_attr->pm_lock);
- #endif
-
- spi_setup_device(host->device[device->id]);
-
- if (host->bus_attr->dma_enabled) {
-
- spicommon_dmaworkaround_transfer_active(host->bus_attr->tx_dma_chan);
- }
- return ESP_OK;
- }
- void SPI_MASTER_ISR_ATTR spi_device_release_bus(spi_device_t *dev)
- {
- spi_host_t *host = dev->host;
- if (spi_bus_device_is_polling(dev)){
- ESP_EARLY_LOGE(SPI_TAG, "Cannot release bus when a polling transaction is in progress.");
- assert(0);
- }
- if (host->bus_attr->dma_enabled) {
-
- spicommon_dmaworkaround_idle(host->bus_attr->tx_dma_chan);
- }
-
-
- #ifdef CONFIG_PM_ENABLE
-
- esp_pm_lock_release(host->bus_attr->pm_lock);
- #endif
- ESP_LOGD(SPI_TAG, "device%d release bus", dev->id);
- host->device_acquiring_lock = NULL;
- esp_err_t ret = spi_bus_lock_acquire_end(dev->dev_lock);
- 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;
- SPI_CHECK(ticks_to_wait == portMAX_DELAY, "currently timeout is not available for polling transactions", ESP_ERR_INVALID_ARG);
- ret = check_trans_valid(handle, trans_desc);
- if (ret!=ESP_OK) return ret;
- SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot send polling transaction while the previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE );
-
- spi_host_t *host = handle->host;
- if (host->device_acquiring_lock != handle) {
- ret = spi_bus_lock_acquire_start(handle->dev_lock, ticks_to_wait);
- } else {
- ret = spi_bus_lock_wait_bg_done(handle->dev_lock, ticks_to_wait);
- }
- if (ret != ESP_OK) return ret;
- ret = setup_priv_desc(trans_desc, &host->cur_trans_buf, (host->bus_attr->dma_enabled));
- if (ret!=ESP_OK) return ret;
-
- host->polling = true;
- ESP_LOGV(SPI_TAG, "polling trans");
- 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);
- spi_host_t *host = handle->host;
- assert(host->cur_cs == handle->id);
- assert(handle == get_acquiring_dev(host));
- TickType_t start = xTaskGetTickCount();
- while (!spi_hal_usr_is_done(&host->hal)) {
- TickType_t end = xTaskGetTickCount();
- if (end - start > ticks_to_wait) {
- return ESP_ERR_TIMEOUT;
- }
- }
- ESP_LOGV(SPI_TAG, "polling trans done");
-
- spi_post_trans(host);
-
- uninstall_priv_desc(&host->cur_trans_buf);
- host->polling = false;
- if (host->device_acquiring_lock != handle) {
- assert(host->device_acquiring_lock == NULL);
- spi_bus_lock_acquire_end(handle->dev_lock);
- }
- 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;
- if (handle->host->mutex) xSemaphoreTake(handle->host->mutex, portMAX_DELAY);
- ret = spi_device_polling_start(handle, trans_desc, portMAX_DELAY);
- if (ret != ESP_OK) {
- if (handle->host->mutex) xSemaphoreGive(handle->host->mutex);
- return ret;
- }
- ret = spi_device_polling_end(handle, portMAX_DELAY);
- if (handle->host->mutex) xSemaphoreGive(handle->host->mutex);
- return ret;
- }
|