components/spidriver/spi_master_lobo.c

changeset 0
b74b0e4902c3
child 1
ad2c8b13eb88
equal deleted inserted replaced
-1:000000000000 0:b74b0e4902c3
1 // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15
16 /*
17 ----------------------------------------
18 Non DMA version of the spi_master driver
19 ----------------------------------------
20 ------------------------------------------------------------------------------------
21 Based on esp-idf 'spi_master', modified by LoBo (https://github.com/loboris) 03/2017
22 ------------------------------------------------------------------------------------
23
24 * Transfers data to SPI device in direct mode, not using DMA
25 * All configuration options (bus, device, transaction) are the same as in spi_master driver
26 * Transfers uses the semaphore (taken in select function & given in deselect function) to protect the transfer
27 * Number of the devices attached to the bus which uses hardware CS can be 3 ('NO_CS')
28 * Additional devices which uses software CS can be attached to the bus, up to 'NO_DEV'
29 * 'spi_bus_initialize' & 'spi_bus_remove' functions are removed, spi bus is initiated/removed in spi_lobo_bus_add_device/spi_lobo_bus_remove_device when needed
30 * 'spi_lobo_bus_add_device' function has added parameter 'bus_config' and automatically initializes spi bus device if not already initialized
31 * 'spi_lobo_bus_remove_device' automatically removes spi bus device if no other devices are attached to it.
32 * Devices can have individual bus_configs, so different mosi, miso, sck pins can be configured for each device
33 Reconfiguring the bus is done automaticaly in 'spi_lobo_device_select' function
34 * 'spi_lobo_device_select' & 'spi_lobo_device_deselect' functions handles devices configuration changes and software CS
35 * Some helper functions are added ('spi_lobo_get_speed', 'spi_lobo_set_speed', ...)
36 * All structures are available in header file for easy creation of user low level spi functions. See **tftfunc.c** source for examples.
37 * Transimt and receive lenghts are limited only by available memory
38
39
40 Main driver's function is 'spi_lobo_transfer_data()'
41
42 * TRANSMIT 8-bit data to spi device from 'trans->tx_buffer' or 'trans->tx_data' (trans->lenght/8 bytes)
43 * and RECEIVE data to 'trans->rx_buffer' or 'trans->rx_data' (trans->rx_length/8 bytes)
44 * Lengths must be 8-bit multiples!
45 * If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data
46 * If trans->tx_buffer is NULL or trans->length is 0, only receives data
47 * If the device is in duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag NOT set), data are transmitted and received simultaneously.
48 * If the device is in half duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag IS set), data are received after transmission
49 * 'address', 'command' and 'dummy bits' are transmitted before data phase IF set in device's configuration
50 * and IF 'trans->length' and 'trans->rx_length' are NOT both 0
51 * If configured, devices 'pre_cb' callback is called before and 'post_cb' after the transmission
52 * If device was not previously selected, it will be selected before transmission and deselected after transmission.
53
54 */
55
56 /*
57 Replace this include with
58 #include "driver/spi_master_lobo.h"
59 if the driver is located in esp-isf/components
60 */
61 #include "freertos/FreeRTOS.h"
62 #include <string.h>
63 #include <stdio.h>
64 #include "soc/gpio_sig_map.h"
65 #include "soc/spi_reg.h"
66 #include "soc/dport_reg.h"
67 #include "soc/rtc_cntl_reg.h"
68 #include "rom/ets_sys.h"
69 #include "esp_types.h"
70 #include "esp_attr.h"
71 #include "esp_log.h"
72 #include "esp_err.h"
73 #include "freertos/semphr.h"
74 #include "freertos/xtensa_api.h"
75 #include "freertos/task.h"
76 #include "freertos/ringbuf.h"
77 #include "soc/soc.h"
78 #include "soc/dport_reg.h"
79 #include "soc/uart_struct.h"
80 #include "driver/uart.h"
81 #include "driver/gpio.h"
82 #include "driver/periph_ctrl.h"
83 #include "esp_heap_caps.h"
84 #include "driver/periph_ctrl.h"
85 #include "spi_master_lobo.h"
86
87
88 static spi_lobo_host_t *spihost[3] = {NULL};
89
90
91 static const char *SPI_TAG = "spi_lobo_master";
92 #define SPI_CHECK(a, str, ret_val) \
93 if (!(a)) { \
94 ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
95 return (ret_val); \
96 }
97
98 /*
99 Stores a bunch of per-spi-peripheral data.
100 */
101 typedef struct {
102 const uint8_t spiclk_out; //GPIO mux output signals
103 const uint8_t spid_out;
104 const uint8_t spiq_out;
105 const uint8_t spiwp_out;
106 const uint8_t spihd_out;
107 const uint8_t spid_in; //GPIO mux input signals
108 const uint8_t spiq_in;
109 const uint8_t spiwp_in;
110 const uint8_t spihd_in;
111 const uint8_t spics_out[3]; // /CS GPIO output mux signals
112 const uint8_t spiclk_native; //IO pins of IO_MUX muxed signals
113 const uint8_t spid_native;
114 const uint8_t spiq_native;
115 const uint8_t spiwp_native;
116 const uint8_t spihd_native;
117 const uint8_t spics0_native;
118 const uint8_t irq; //irq source for interrupt mux
119 const uint8_t irq_dma; //dma irq source for interrupt mux
120 const periph_module_t module; //peripheral module, for enabling clock etc
121 spi_dev_t *hw; //Pointer to the hardware registers
122 } spi_signal_conn_t;
123
124 /*
125 Bunch of constants for every SPI peripheral: GPIO signals, irqs, hw addr of registers etc
126 */
127 static const spi_signal_conn_t io_signal[3]={
128 {
129 .spiclk_out=SPICLK_OUT_IDX,
130 .spid_out=SPID_OUT_IDX,
131 .spiq_out=SPIQ_OUT_IDX,
132 .spiwp_out=SPIWP_OUT_IDX,
133 .spihd_out=SPIHD_OUT_IDX,
134 .spid_in=SPID_IN_IDX,
135 .spiq_in=SPIQ_IN_IDX,
136 .spiwp_in=SPIWP_IN_IDX,
137 .spihd_in=SPIHD_IN_IDX,
138 .spics_out={SPICS0_OUT_IDX, SPICS1_OUT_IDX, SPICS2_OUT_IDX},
139 .spiclk_native=6,
140 .spid_native=8,
141 .spiq_native=7,
142 .spiwp_native=10,
143 .spihd_native=9,
144 .spics0_native=11,
145 .irq=ETS_SPI1_INTR_SOURCE,
146 .irq_dma=ETS_SPI1_DMA_INTR_SOURCE,
147 .module=PERIPH_SPI_MODULE,
148 .hw=&SPI1
149 }, {
150 .spiclk_out=HSPICLK_OUT_IDX,
151 .spid_out=HSPID_OUT_IDX,
152 .spiq_out=HSPIQ_OUT_IDX,
153 .spiwp_out=HSPIWP_OUT_IDX,
154 .spihd_out=HSPIHD_OUT_IDX,
155 .spid_in=HSPID_IN_IDX,
156 .spiq_in=HSPIQ_IN_IDX,
157 .spiwp_in=HSPIWP_IN_IDX,
158 .spihd_in=HSPIHD_IN_IDX,
159 .spics_out={HSPICS0_OUT_IDX, HSPICS1_OUT_IDX, HSPICS2_OUT_IDX},
160 .spiclk_native=14,
161 .spid_native=13,
162 .spiq_native=12,
163 .spiwp_native=2,
164 .spihd_native=4,
165 .spics0_native=15,
166 .irq=ETS_SPI2_INTR_SOURCE,
167 .irq_dma=ETS_SPI2_DMA_INTR_SOURCE,
168 .module=PERIPH_HSPI_MODULE,
169 .hw=&SPI2
170 }, {
171 .spiclk_out=VSPICLK_OUT_IDX,
172 .spid_out=VSPID_OUT_IDX,
173 .spiq_out=VSPIQ_OUT_IDX,
174 .spiwp_out=VSPIWP_OUT_IDX,
175 .spihd_out=VSPIHD_OUT_IDX,
176 .spid_in=VSPID_IN_IDX,
177 .spiq_in=VSPIQ_IN_IDX,
178 .spiwp_in=VSPIWP_IN_IDX,
179 .spihd_in=VSPIHD_IN_IDX,
180 .spics_out={VSPICS0_OUT_IDX, VSPICS1_OUT_IDX, VSPICS2_OUT_IDX},
181 .spiclk_native=18,
182 .spid_native=23,
183 .spiq_native=19,
184 .spiwp_native=22,
185 .spihd_native=21,
186 .spics0_native=5,
187 .irq=ETS_SPI3_INTR_SOURCE,
188 .irq_dma=ETS_SPI3_DMA_INTR_SOURCE,
189 .module=PERIPH_VSPI_MODULE,
190 .hw=&SPI3
191 }
192 };
193
194
195 //======================================================================================================
196
197 #define DMA_CHANNEL_ENABLED(dma_chan) (BIT(dma_chan-1))
198
199 typedef void(*dmaworkaround_cb_t)(void *arg);
200
201 //Set up a list of dma descriptors. dmadesc is an array of descriptors. Data is the buffer to point to.
202 //--------------------------------------------------------------------------------------------
203 void spi_lobo_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx)
204 {
205 int n = 0;
206 while (len) {
207 int dmachunklen = len;
208 if (dmachunklen > SPI_MAX_DMA_LEN) dmachunklen = SPI_MAX_DMA_LEN;
209 if (isrx) {
210 //Receive needs DMA length rounded to next 32-bit boundary
211 dmadesc[n].size = (dmachunklen + 3) & (~3);
212 dmadesc[n].length = (dmachunklen + 3) & (~3);
213 } else {
214 dmadesc[n].size = dmachunklen;
215 dmadesc[n].length = dmachunklen;
216 }
217 dmadesc[n].buf = (uint8_t *)data;
218 dmadesc[n].eof = 0;
219 dmadesc[n].sosf = 0;
220 dmadesc[n].owner = 1;
221 dmadesc[n].qe.stqe_next = &dmadesc[n + 1];
222 len -= dmachunklen;
223 data += dmachunklen;
224 n++;
225 }
226 dmadesc[n - 1].eof = 1; //Mark last DMA desc as end of stream.
227 dmadesc[n - 1].qe.stqe_next = NULL;
228 }
229
230
231 /*
232 Code for workaround for DMA issue in ESP32 v0/v1 silicon
233 */
234
235
236 static volatile int dmaworkaround_channels_busy[2] = {0, 0};
237 static dmaworkaround_cb_t dmaworkaround_cb;
238 static void *dmaworkaround_cb_arg;
239 static portMUX_TYPE dmaworkaround_mux = portMUX_INITIALIZER_UNLOCKED;
240 static int dmaworkaround_waiting_for_chan = 0;
241 static bool spi_periph_claimed[3] = {true, false, false};
242 static uint8_t spi_dma_chan_enabled = 0;
243 static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED;
244
245 //--------------------------------------------------------------------------------------------
246 bool IRAM_ATTR spi_lobo_dmaworkaround_req_reset(int dmachan, dmaworkaround_cb_t cb, void *arg)
247 {
248 int otherchan = (dmachan == 1) ? 2 : 1;
249 bool ret;
250 portENTER_CRITICAL(&dmaworkaround_mux);
251 if (dmaworkaround_channels_busy[otherchan-1]) {
252 //Other channel is busy. Call back when it's done.
253 dmaworkaround_cb = cb;
254 dmaworkaround_cb_arg = arg;
255 dmaworkaround_waiting_for_chan = otherchan;
256 ret = false;
257 } else {
258 //Reset DMA
259 DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
260 DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
261 ret = true;
262 }
263 portEXIT_CRITICAL(&dmaworkaround_mux);
264 return ret;
265 }
266
267 //-------------------------------------------------------
268 bool IRAM_ATTR spi_lobo_dmaworkaround_reset_in_progress()
269 {
270 return (dmaworkaround_waiting_for_chan != 0);
271 }
272
273 //-----------------------------------------------------
274 void IRAM_ATTR spi_lobo_dmaworkaround_idle(int dmachan)
275 {
276 portENTER_CRITICAL(&dmaworkaround_mux);
277 dmaworkaround_channels_busy[dmachan-1] = 0;
278 if (dmaworkaround_waiting_for_chan == dmachan) {
279 //Reset DMA
280 DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
281 DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
282 dmaworkaround_waiting_for_chan = 0;
283 //Call callback
284 dmaworkaround_cb(dmaworkaround_cb_arg);
285
286 }
287 portEXIT_CRITICAL(&dmaworkaround_mux);
288 }
289
290 //----------------------------------------------------------------
291 void IRAM_ATTR spi_lobo_dmaworkaround_transfer_active(int dmachan)
292 {
293 portENTER_CRITICAL(&dmaworkaround_mux);
294 dmaworkaround_channels_busy[dmachan-1] = 1;
295 portEXIT_CRITICAL(&dmaworkaround_mux);
296 }
297
298 //Returns true if this peripheral is successfully claimed, false if otherwise.
299 //-----------------------------------------------------
300 bool spi_lobo_periph_claim(spi_lobo_host_device_t host)
301 {
302 bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], false, true);
303 if (ret) periph_module_enable(io_signal[host].module);
304 return ret;
305 }
306
307 //Returns true if this peripheral is successfully freed, false if otherwise.
308 //-----------------------------------------------
309 bool spi_lobo_periph_free(spi_lobo_host_device_t host)
310 {
311 bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], true, false);
312 if (ret) periph_module_disable(io_signal[host].module);
313 return ret;
314 }
315
316 //-----------------------------------------
317 bool spi_lobo_dma_chan_claim (int dma_chan)
318 {
319 bool ret = false;
320 assert( dma_chan == 1 || dma_chan == 2 );
321
322 portENTER_CRITICAL(&spi_dma_spinlock);
323 if ( !(spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan)) ) {
324 // get the channel only when it's not claimed yet.
325 spi_dma_chan_enabled |= DMA_CHANNEL_ENABLED(dma_chan);
326 ret = true;
327 }
328 periph_module_enable( PERIPH_SPI_DMA_MODULE );
329 portEXIT_CRITICAL(&spi_dma_spinlock);
330
331 return ret;
332 }
333
334 //---------------------------------------
335 bool spi_lobo_dma_chan_free(int dma_chan)
336 {
337 assert( dma_chan == 1 || dma_chan == 2 );
338 assert( spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan) );
339
340 portENTER_CRITICAL(&spi_dma_spinlock);
341 spi_dma_chan_enabled &= ~DMA_CHANNEL_ENABLED(dma_chan);
342 if ( spi_dma_chan_enabled == 0 ) {
343 //disable the DMA only when all the channels are freed.
344 periph_module_disable( PERIPH_SPI_DMA_MODULE );
345 }
346 portEXIT_CRITICAL(&spi_dma_spinlock);
347
348 return true;
349 }
350
351
352 //======================================================================================================
353
354
355 //----------------------------------------------------------------------------------------------------------------
356 static esp_err_t spi_lobo_bus_initialize(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, int init)
357 {
358 bool native=true, spi_chan_claimed, dma_chan_claimed;
359
360 if (init > 0) {
361 /* ToDo: remove this when we have flash operations cooperating with this */
362 SPI_CHECK(host!=TFT_SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED);
363
364 SPI_CHECK(host>=TFT_SPI_HOST && host<=TFT_VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
365 SPI_CHECK(spihost[host]==NULL, "host already in use", ESP_ERR_INVALID_STATE);
366 }
367 else {
368 SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE);
369 }
370
371 SPI_CHECK(bus_config->mosi_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG);
372 SPI_CHECK(bus_config->sclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->sclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG);
373 SPI_CHECK(bus_config->miso_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->miso_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG);
374 SPI_CHECK(bus_config->quadwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG);
375 SPI_CHECK(bus_config->quadhd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadhd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG);
376
377 if (init > 0) {
378 spi_chan_claimed=spi_lobo_periph_claim(host);
379 SPI_CHECK(spi_chan_claimed, "host already in use", ESP_ERR_INVALID_STATE);
380
381 //spihost[host]=malloc(sizeof(spi_lobo_host_t));
382 spihost[host]=heap_caps_malloc(sizeof(spi_lobo_host_t), MALLOC_CAP_DMA);
383 if (spihost[host]==NULL) return ESP_ERR_NO_MEM;
384 memset(spihost[host], 0, sizeof(spi_lobo_host_t));
385 // Create semaphore
386 spihost[host]->spi_lobo_bus_mutex = xSemaphoreCreateMutex();
387 if (!spihost[host]->spi_lobo_bus_mutex) return ESP_ERR_NO_MEM;
388 }
389
390 spihost[host]->cur_device = -1;
391 memcpy(&spihost[host]->cur_bus_config, bus_config, sizeof(spi_lobo_bus_config_t));
392
393 //Check if the selected pins correspond to the native pins of the peripheral
394 if (bus_config->mosi_io_num >= 0 && bus_config->mosi_io_num!=io_signal[host].spid_native) native=false;
395 if (bus_config->miso_io_num >= 0 && bus_config->miso_io_num!=io_signal[host].spiq_native) native=false;
396 if (bus_config->sclk_io_num >= 0 && bus_config->sclk_io_num!=io_signal[host].spiclk_native) native=false;
397 if (bus_config->quadwp_io_num >= 0 && bus_config->quadwp_io_num!=io_signal[host].spiwp_native) native=false;
398 if (bus_config->quadhd_io_num >= 0 && bus_config->quadhd_io_num!=io_signal[host].spihd_native) native=false;
399
400 spihost[host]->no_gpio_matrix=native;
401 if (native) {
402 //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure
403 //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway.
404 if (bus_config->mosi_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], 1);
405 if (bus_config->miso_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], 1);
406 if (bus_config->quadwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], 1);
407 if (bus_config->quadhd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], 1);
408 if (bus_config->sclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], 1);
409 } else {
410 //Use GPIO
411 if (bus_config->mosi_io_num>0) {
412 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], PIN_FUNC_GPIO);
413 gpio_set_direction(bus_config->mosi_io_num, GPIO_MODE_OUTPUT);
414 gpio_matrix_out(bus_config->mosi_io_num, io_signal[host].spid_out, false, false);
415 gpio_matrix_in(bus_config->mosi_io_num, io_signal[host].spid_in, false);
416 }
417 if (bus_config->miso_io_num>0) {
418 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO);
419 gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT);
420 gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false);
421 gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false);
422 }
423 if (bus_config->quadwp_io_num>0) {
424 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], PIN_FUNC_GPIO);
425 gpio_set_direction(bus_config->quadwp_io_num, GPIO_MODE_OUTPUT);
426 gpio_matrix_out(bus_config->quadwp_io_num, io_signal[host].spiwp_out, false, false);
427 gpio_matrix_in(bus_config->quadwp_io_num, io_signal[host].spiwp_in, false);
428 }
429 if (bus_config->quadhd_io_num>0) {
430 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], PIN_FUNC_GPIO);
431 gpio_set_direction(bus_config->quadhd_io_num, GPIO_MODE_OUTPUT);
432 gpio_matrix_out(bus_config->quadhd_io_num, io_signal[host].spihd_out, false, false);
433 gpio_matrix_in(bus_config->quadhd_io_num, io_signal[host].spihd_in, false);
434 }
435 if (bus_config->sclk_io_num>0) {
436 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], PIN_FUNC_GPIO);
437 gpio_set_direction(bus_config->sclk_io_num, GPIO_MODE_OUTPUT);
438 gpio_matrix_out(bus_config->sclk_io_num, io_signal[host].spiclk_out, false, false);
439 }
440 }
441 periph_module_enable(io_signal[host].module);
442 spihost[host]->hw=io_signal[host].hw;
443
444 if (init > 0) {
445 dma_chan_claimed=spi_lobo_dma_chan_claim(init);
446 if ( !dma_chan_claimed ) {
447 spi_lobo_periph_free( host );
448 SPI_CHECK(dma_chan_claimed, "dma channel already in use", ESP_ERR_INVALID_STATE);
449 }
450 spihost[host]->dma_chan = init;
451 //See how many dma descriptors we need and allocate them
452 int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN;
453 if (dma_desc_ct==0) dma_desc_ct=1; //default to 4k when max is not given
454 spihost[host]->max_transfer_sz = dma_desc_ct*SPI_MAX_DMA_LEN;
455
456 spihost[host]->dmadesc_tx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
457 spihost[host]->dmadesc_rx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
458 if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem;
459
460 //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset.
461 spi_lobo_dmaworkaround_idle(spihost[host]->dma_chan);
462
463 // Reset DMA
464 spihost[host]->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST;
465 spihost[host]->hw->dma_out_link.start=0;
466 spihost[host]->hw->dma_in_link.start=0;
467 spihost[host]->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST);
468 spihost[host]->hw->dma_conf.out_data_burst_en=1;
469
470 //Reset timing
471 spihost[host]->hw->ctrl2.val=0;
472
473 //Disable unneeded ints
474 spihost[host]->hw->slave.rd_buf_done=0;
475 spihost[host]->hw->slave.wr_buf_done=0;
476 spihost[host]->hw->slave.rd_sta_done=0;
477 spihost[host]->hw->slave.wr_sta_done=0;
478 spihost[host]->hw->slave.rd_buf_inten=0;
479 spihost[host]->hw->slave.wr_buf_inten=0;
480 spihost[host]->hw->slave.rd_sta_inten=0;
481 spihost[host]->hw->slave.wr_sta_inten=0;
482
483 //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
484 //disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling
485 //any transactions that are queued.
486 spihost[host]->hw->slave.trans_inten=1;
487 spihost[host]->hw->slave.trans_done=1;
488
489 //Select DMA channel.
490 DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, init, (host * 2));
491 }
492 return ESP_OK;
493
494 nomem:
495 if (spihost[host]) {
496 free(spihost[host]->dmadesc_tx);
497 free(spihost[host]->dmadesc_rx);
498 }
499 free(spihost[host]);
500 spi_lobo_periph_free(host);
501 return ESP_ERR_NO_MEM;
502 }
503
504 //---------------------------------------------------------------------------
505 static esp_err_t spi_lobo_bus_free(spi_lobo_host_device_t host, int dofree)
506 {
507 if ((host == TFT_SPI_HOST) || (host > TFT_VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host
508
509 if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE; // host not in use
510
511 if (dofree) {
512 for (int x=0; x<NO_DEV; x++) {
513 if (spihost[host]->device[x] != NULL) return ESP_ERR_INVALID_STATE; // not all devices freed
514 }
515 }
516 if ( spihost[host]->dma_chan > 0 ) {
517 spi_lobo_dma_chan_free ( spihost[host]->dma_chan );
518 }
519 spihost[host]->hw->slave.trans_inten=0;
520 spihost[host]->hw->slave.trans_done=0;
521 spi_lobo_periph_free(host);
522
523 if (dofree) {
524 vSemaphoreDelete(spihost[host]->spi_lobo_bus_mutex);
525 free(spihost[host]->dmadesc_tx);
526 free(spihost[host]->dmadesc_rx);
527 free(spihost[host]);
528 spihost[host] = NULL;
529 }
530 return ESP_OK;
531 }
532
533 //---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
534 esp_err_t spi_lobo_bus_add_device(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, spi_lobo_device_interface_config_t *dev_config, spi_lobo_device_handle_t *handle)
535 {
536 if ((host == TFT_SPI_HOST) || (host > TFT_VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host
537
538 if (spihost[host] == NULL) {
539 esp_err_t ret = spi_lobo_bus_initialize(host, bus_config, 1);
540 if (ret) return ret;
541 }
542
543 int freecs, maxdev;
544 int apbclk=APB_CLK_FREQ;
545
546 if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE;
547
548 if (dev_config->spics_io_num >= 0) {
549 if (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num)) return ESP_ERR_INVALID_ARG;
550 if (dev_config->spics_ext_io_num > 0) dev_config->spics_ext_io_num = -1;
551 }
552 else {
553 //if ((dev_config->spics_ext_io_num <= 0) || (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_ext_io_num))) return ESP_ERR_INVALID_ARG;
554 }
555
556 //ToDo: Check if some other device uses the same 'spics_ext_io_num'
557
558 if (dev_config->clock_speed_hz == 0) return ESP_ERR_INVALID_ARG;
559 if (dev_config->spics_io_num > 0) maxdev = NO_CS;
560 else maxdev = NO_DEV;
561
562 for (freecs=0; freecs<maxdev; freecs++) {
563 //See if this slot is free; reserve if it is by putting a dummy pointer in the slot. We use an atomic compare&swap to make this thread-safe.
564 if (__sync_bool_compare_and_swap(&spihost[host]->device[freecs], NULL, (spi_lobo_device_t *)1)) break;
565 }
566 if (freecs == maxdev) return ESP_ERR_NOT_FOUND;
567
568 // The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
569 // duplex mode does absolutely nothing on the ESP32.
570 if ((dev_config->cs_ena_pretrans != 0) && (dev_config->flags & LB_SPI_DEVICE_HALFDUPLEX)) return ESP_ERR_INVALID_ARG;
571
572 // Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
573 if (((dev_config->flags & LB_SPI_DEVICE_HALFDUPLEX)==0) && (dev_config->clock_speed_hz > ((apbclk*2)/5)) && (!spihost[host]->no_gpio_matrix)) return ESP_ERR_INVALID_ARG;
574
575 //Allocate memory for device
576 spi_lobo_device_t *dev=malloc(sizeof(spi_lobo_device_t));
577 if (dev==NULL) return ESP_ERR_NO_MEM;
578
579 memset(dev, 0, sizeof(spi_lobo_device_t));
580 spihost[host]->device[freecs]=dev;
581
582 if (dev_config->duty_cycle_pos==0) dev_config->duty_cycle_pos=128;
583 dev->host=spihost[host];
584 dev->host_dev = host;
585
586 //We want to save a copy of the dev config in the dev struct.
587 memcpy(&dev->cfg, dev_config, sizeof(spi_lobo_device_interface_config_t));
588 //We want to save a copy of the bus config in the dev struct.
589 memcpy(&dev->bus_config, bus_config, sizeof(spi_lobo_bus_config_t));
590
591 //Set CS pin, CS options
592 if (dev_config->spics_io_num > 0) {
593 if (spihost[host]->no_gpio_matrix &&dev_config->spics_io_num == io_signal[host].spics0_native && freecs==0) {
594 //Again, the cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define.
595 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], 1);
596 } else {
597 //Use GPIO matrix
598 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], PIN_FUNC_GPIO);
599 gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT);
600 gpio_matrix_out(dev_config->spics_io_num, io_signal[host].spics_out[freecs], false, false);
601 }
602 }
603 else if (dev_config->spics_ext_io_num >= 0) {
604 gpio_set_direction(dev_config->spics_ext_io_num, GPIO_MODE_OUTPUT);
605 gpio_set_level(dev_config->spics_ext_io_num, 1);
606 }
607 if (dev_config->flags & LB_SPI_DEVICE_CLK_AS_CS) {
608 spihost[host]->hw->pin.master_ck_sel |= (1<<freecs);
609 } else {
610 spihost[host]->hw->pin.master_ck_sel &= (1<<freecs);
611 }
612 if (dev_config->flags & LB_SPI_DEVICE_POSITIVE_CS) {
613 spihost[host]->hw->pin.master_cs_pol |= (1<<freecs);
614 } else {
615 spihost[host]->hw->pin.master_cs_pol &= (1<<freecs);
616 }
617
618 *handle = dev;
619 return ESP_OK;
620 }
621
622 //-------------------------------------------------------------------
623 esp_err_t spi_lobo_bus_remove_device(spi_lobo_device_handle_t handle)
624 {
625 int x;
626 if (handle == NULL) return ESP_ERR_INVALID_ARG;
627
628 //Remove device from list of csses and free memory
629 for (x=0; x<NO_DEV; x++) {
630 if (handle->host->device[x] == handle) handle->host->device[x]=NULL;
631 }
632
633 // Check if all devices are removed from this host and free the bus if yes
634 for (x=0; x<NO_DEV; x++) {
635 if (spihost[handle->host_dev]->device[x] !=NULL) break;
636 }
637 if (x == NO_DEV) {
638 free(handle);
639 spi_lobo_bus_free(handle->host_dev, 1);
640 }
641 else free(handle);
642
643 return ESP_OK;
644 }
645
646 //-----------------------------------------------------------------
647 static int IRAM_ATTR spi_freq_for_pre_n(int fapb, int pre, int n) {
648 return (fapb / (pre * n));
649 }
650
651 /*
652 * Set the SPI clock to a certain frequency. Returns the effective frequency set, which may be slightly
653 * different from the requested frequency.
654 */
655 //-----------------------------------------------------------------------------------
656 static int IRAM_ATTR spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) {
657 int pre, n, h, l, eff_clk;
658
659 //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value.
660 if (hz>((fapb/4)*3)) {
661 //Using Fapb directly will give us the best result here.
662 hw->clock.clkcnt_l=0;
663 hw->clock.clkcnt_h=0;
664 hw->clock.clkcnt_n=0;
665 hw->clock.clkdiv_pre=0;
666 hw->clock.clk_equ_sysclk=1;
667 eff_clk=fapb;
668 } else {
669 //For best duty cycle resolution, we want n to be as close to 32 as possible, but
670 //we also need a pre/n combo that gets us as close as possible to the intended freq.
671 //To do this, we bruteforce n and calculate the best pre to go along with that.
672 //If there's a choice between pre/n combos that give the same result, use the one
673 //with the higher n.
674 int bestn=-1;
675 int bestpre=-1;
676 int besterr=0;
677 int errval;
678 for (n=1; n<=64; n++) {
679 //Effectively, this does pre=round((fapb/n)/hz).
680 pre=((fapb/n)+(hz/2))/hz;
681 if (pre<=0) pre=1;
682 if (pre>8192) pre=8192;
683 errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz);
684 if (bestn==-1 || errval<=besterr) {
685 besterr=errval;
686 bestn=n;
687 bestpre=pre;
688 }
689 }
690
691 n=bestn;
692 pre=bestpre;
693 l=n;
694 //This effectively does round((duty_cycle*n)/256)
695 h=(duty_cycle*n+127)/256;
696 if (h<=0) h=1;
697
698 hw->clock.clk_equ_sysclk=0;
699 hw->clock.clkcnt_n=n-1;
700 hw->clock.clkdiv_pre=pre-1;
701 hw->clock.clkcnt_h=h-1;
702 hw->clock.clkcnt_l=l-1;
703 eff_clk=spi_freq_for_pre_n(fapb, pre, n);
704 }
705 return eff_clk;
706 }
707
708
709
710 //------------------------------------------------------------------------------------
711 esp_err_t IRAM_ATTR spi_lobo_device_select(spi_lobo_device_handle_t handle, int force)
712 {
713 if (handle == NULL) return ESP_ERR_INVALID_ARG;
714
715 if ((handle->cfg.selected == 1) && (!force)) return ESP_OK; // already selected
716
717 int i;
718 spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
719
720 // find device's host bus
721 for (i=0; i<NO_DEV; i++) {
722 if (host->device[i] == handle) break;
723 }
724 if (i == NO_DEV) return ESP_ERR_INVALID_ARG;
725
726 if (!(xSemaphoreTake(host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE;
727
728 // Check if previously used device's bus device is the same
729 if (memcmp(&host->cur_bus_config, &handle->bus_config, sizeof(spi_lobo_bus_config_t)) != 0) {
730 // device has different bus configuration, we need to reconfigure the bus
731 esp_err_t err = spi_lobo_bus_free(1, 0);
732 if (err) {
733 xSemaphoreGive(host->spi_lobo_bus_mutex);
734 return err;
735 }
736 err = spi_lobo_bus_initialize(i, &handle->bus_config, -1);
737 if (err) {
738 xSemaphoreGive(host->spi_lobo_bus_mutex);
739 return err;
740 }
741 }
742
743 //Reconfigure according to device settings, but only if the device changed or forced.
744 if ((force) || (host->device[host->cur_device] != handle)) {
745 //Assumes a hardcoded 80MHz Fapb for now. ToDo: figure out something better once we have clock scaling working.
746 int apbclk=APB_CLK_FREQ;
747
748 //Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
749 if (((handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX) == 0) && (handle->cfg.clock_speed_hz > ((apbclk*2)/5)) && (!host->no_gpio_matrix)) {
750 // set speed to 32 MHz
751 handle->cfg.clock_speed_hz = (apbclk*2)/5;
752 }
753
754 int effclk=spi_set_clock(host->hw, apbclk, handle->cfg.clock_speed_hz, handle->cfg.duty_cycle_pos);
755 //Configure bit order
756 host->hw->ctrl.rd_bit_order=(handle->cfg.flags & LB_SPI_DEVICE_RXBIT_LSBFIRST)?1:0;
757 host->hw->ctrl.wr_bit_order=(handle->cfg.flags & LB_SPI_DEVICE_TXBIT_LSBFIRST)?1:0;
758
759 //Configure polarity
760 //SPI iface needs to be configured for a delay in some cases.
761 int nodelay=0;
762 int extra_dummy=0;
763 if (host->no_gpio_matrix) {
764 if (effclk >= apbclk/2) {
765 nodelay=1;
766 }
767 } else {
768 if (effclk >= apbclk/2) {
769 nodelay=1;
770 extra_dummy=1; //Note: This only works on half-duplex connections. spi_lobo_bus_add_device checks for this.
771 } else if (effclk >= apbclk/4) {
772 nodelay=1;
773 }
774 }
775 if (handle->cfg.mode==0) {
776 host->hw->pin.ck_idle_edge=0;
777 host->hw->user.ck_out_edge=0;
778 host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
779 } else if (handle->cfg.mode==1) {
780 host->hw->pin.ck_idle_edge=0;
781 host->hw->user.ck_out_edge=1;
782 host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
783 } else if (handle->cfg.mode==2) {
784 host->hw->pin.ck_idle_edge=1;
785 host->hw->user.ck_out_edge=1;
786 host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
787 } else if (handle->cfg.mode==3) {
788 host->hw->pin.ck_idle_edge=1;
789 host->hw->user.ck_out_edge=0;
790 host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
791 }
792
793 //Configure bit sizes, load addr and command
794 host->hw->user.usr_dummy=(handle->cfg.dummy_bits+extra_dummy)?1:0;
795 host->hw->user.usr_addr=(handle->cfg.address_bits)?1:0;
796 host->hw->user.usr_command=(handle->cfg.command_bits)?1:0;
797 host->hw->user1.usr_addr_bitlen=handle->cfg.address_bits-1;
798 host->hw->user1.usr_dummy_cyclelen=handle->cfg.dummy_bits+extra_dummy-1;
799 host->hw->user2.usr_command_bitlen=handle->cfg.command_bits-1;
800 //Configure misc stuff
801 host->hw->user.doutdin=(handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)?0:1;
802 host->hw->user.sio=(handle->cfg.flags & LB_SPI_DEVICE_3WIRE)?1:0;
803
804 host->hw->ctrl2.setup_time=handle->cfg.cs_ena_pretrans-1;
805 host->hw->user.cs_setup=handle->cfg.cs_ena_pretrans?1:0;
806 host->hw->ctrl2.hold_time=handle->cfg.cs_ena_posttrans-1;
807 host->hw->user.cs_hold=(handle->cfg.cs_ena_posttrans)?1:0;
808
809 //Configure CS pin
810 host->hw->pin.cs0_dis=(i==0)?0:1;
811 host->hw->pin.cs1_dis=(i==1)?0:1;
812 host->hw->pin.cs2_dis=(i==2)?0:1;
813
814 host->cur_device = i;
815 }
816
817 if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) {
818 gpio_set_level(handle->cfg.spics_ext_io_num, 0);
819 }
820
821 handle->cfg.selected = 1;
822
823 return ESP_OK;
824 }
825
826 //---------------------------------------------------------------------------
827 esp_err_t IRAM_ATTR spi_lobo_device_deselect(spi_lobo_device_handle_t handle)
828 {
829 if (handle == NULL) return ESP_ERR_INVALID_ARG;
830
831 if (handle->cfg.selected == 0) return ESP_OK; // already deselected
832
833 int i;
834 spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
835
836 for (i=0; i<NO_DEV; i++) {
837 if (host->device[i] == handle) break;
838 }
839 if (i == NO_DEV) return ESP_ERR_INVALID_ARG;
840
841 if (host->device[host->cur_device] == handle) {
842 if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) {
843 gpio_set_level(handle->cfg.spics_ext_io_num, 1);
844 }
845 }
846
847 handle->cfg.selected = 0;
848 xSemaphoreGive(host->spi_lobo_bus_mutex);
849
850 return ESP_OK;
851 }
852
853 //--------------------------------------------------------------------------------
854 esp_err_t IRAM_ATTR spi_lobo_device_TakeSemaphore(spi_lobo_device_handle_t handle)
855 {
856 if (!(xSemaphoreTake(handle->host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE;
857 else return ESP_OK;
858 }
859
860 //---------------------------------------------------------------------------
861 void IRAM_ATTR spi_lobo_device_GiveSemaphore(spi_lobo_device_handle_t handle)
862 {
863 xSemaphoreTake(handle->host->spi_lobo_bus_mutex, portMAX_DELAY);
864 }
865
866 //----------------------------------------------------------
867 uint32_t spi_lobo_get_speed(spi_lobo_device_handle_t handle)
868 {
869 spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
870 uint32_t speed = 0;
871 if (spi_lobo_device_select(handle, 0) == ESP_OK) {
872 if (host->hw->clock.clk_equ_sysclk == 1) speed = 80000000;
873 else speed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1);
874 }
875 spi_lobo_device_deselect(handle);
876 return speed;
877 }
878
879 //--------------------------------------------------------------------------
880 uint32_t spi_lobo_set_speed(spi_lobo_device_handle_t handle, uint32_t speed)
881 {
882 spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
883 uint32_t newspeed = 0;
884 if (spi_lobo_device_select(handle, 0) == ESP_OK) {
885 spi_lobo_device_deselect(handle);
886 handle->cfg.clock_speed_hz = speed;
887 if (spi_lobo_device_select(handle, 1) == ESP_OK) {
888 if (host->hw->clock.clk_equ_sysclk == 1) newspeed = 80000000;
889 else newspeed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1);
890 }
891 }
892 spi_lobo_device_deselect(handle);
893
894 return newspeed;
895 }
896
897 //-------------------------------------------------------------
898 bool spi_lobo_uses_native_pins(spi_lobo_device_handle_t handle)
899 {
900 return handle->host->no_gpio_matrix;
901 }
902
903 //-------------------------------------------------------------------
904 void spi_lobo_get_native_pins(int host, int *sdi, int *sdo, int *sck)
905 {
906 *sdo = io_signal[host].spid_native;
907 *sdi = io_signal[host].spiq_native;
908 *sck = io_signal[host].spiclk_native;
909 }
910
911 /*
912 When using 'spi_lobo_transfer_data' function we can have several scenarios:
913
914 A: Send only (trans->rxlength = 0)
915 B: Receive only (trans->txlength = 0)
916 C: Send & receive (trans->txlength > 0 & trans->rxlength > 0)
917 D: No operation (trans->txlength = 0 & trans->rxlength = 0)
918
919 */
920 //----------------------------------------------------------------------------------------------------------
921 esp_err_t IRAM_ATTR spi_lobo_transfer_data(spi_lobo_device_handle_t handle, spi_lobo_transaction_t *trans) {
922 if (!handle) return ESP_ERR_INVALID_ARG;
923
924 // *** For now we can only handle 8-bit bytes transmission
925 if (((trans->length % 8) != 0) || ((trans->rxlength % 8) != 0)) return ESP_ERR_INVALID_ARG;
926
927 spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
928 esp_err_t ret;
929 uint8_t do_deselect = 0;
930 const uint8_t *txbuffer = NULL;
931 uint8_t *rxbuffer = NULL;
932
933 if (trans->flags & LB_SPI_TRANS_USE_TXDATA) {
934 // Send data from 'trans->tx_data'
935 txbuffer=(uint8_t*)&trans->tx_data[0];
936 } else {
937 // Send data from 'trans->tx_buffer'
938 txbuffer=(uint8_t*)trans->tx_buffer;
939 }
940 if (trans->flags & LB_SPI_TRANS_USE_RXDATA) {
941 // Receive data to 'trans->rx_data'
942 rxbuffer=(uint8_t*)&trans->rx_data[0];
943 } else {
944 // Receive data to 'trans->rx_buffer'
945 rxbuffer=(uint8_t*)trans->rx_buffer;
946 }
947
948 // ** Set transmit & receive length in bytes
949 uint32_t txlen = trans->length / 8;
950 uint32_t rxlen = trans->rxlength / 8;
951
952 if (txbuffer == NULL) txlen = 0;
953 if (rxbuffer == NULL) rxlen = 0;
954 if ((rxlen == 0) && (txlen == 0)) {
955 // ** NOTHING TO SEND or RECEIVE, return
956 return ESP_ERR_INVALID_ARG;
957 }
958
959 // If using 'trans->tx_data' and/or 'trans->rx_data', maximum 4 bytes can be sent/received
960 if ((txbuffer == &trans->tx_data[0]) && (txlen > 4)) return ESP_ERR_INVALID_ARG;
961 if ((rxbuffer == &trans->rx_data[0]) && (rxlen > 4)) return ESP_ERR_INVALID_ARG;
962
963 // --- Wait for SPI bus ready ---
964 while (host->hw->cmd.usr);
965
966 // ** If the device was not selected, select it
967 if (handle->cfg.selected == 0) {
968 ret = spi_lobo_device_select(handle, 0);
969 if (ret) return ret;
970 do_deselect = 1; // We will deselect the device after the operation !
971 }
972
973 // ** Call pre-transmission callback, if any
974 if (handle->cfg.pre_cb) handle->cfg.pre_cb(trans);
975
976 // Test if operating in full duplex mode
977 uint8_t duplex = 1;
978 if (handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX) duplex = 0; // Half duplex mode !
979
980 uint32_t bits, rdbits;
981 uint32_t wd;
982 uint8_t bc, rdidx;
983 uint32_t rdcount = rxlen; // Total number of bytes to read
984 uint32_t count = 0; // number of bytes transmitted
985 uint32_t rd_read = 0; // Number of bytes read so far
986
987 host->hw->user.usr_mosi_highpart = 0; // use the whole spi buffer
988
989 // ** Check if address phase will be used
990 host->hw->user2.usr_command_value=trans->command;
991 if (handle->cfg.address_bits>32) {
992 host->hw->addr=trans->address >> 32;
993 host->hw->slv_wr_status=trans->address & 0xffffffff;
994 } else {
995 host->hw->addr=trans->address & 0xffffffff;
996 }
997
998 // Check if we have to transmit some data
999 if (txlen > 0) {
1000 host->hw->user.usr_mosi = 1;
1001 uint8_t idx;
1002 bits = 0; // remaining bits to send
1003 idx = 0; // index to spi hw data_buf (16 32-bit words, 64 bytes, 512 bits)
1004
1005 // ** Transmit 'txlen' bytes
1006 while (count < txlen) {
1007 wd = 0;
1008 for (bc=0;bc<32;bc+=8) {
1009 wd |= (uint32_t)txbuffer[count] << bc;
1010 count++; // Increment sent data count
1011 bits += 8; // Increment bits count
1012 if (count == txlen) break; // If all transmit data pushed to hw spi buffer break from the loop
1013 }
1014 host->hw->data_buf[idx] = wd;
1015 idx++;
1016 if (idx == 16) {
1017 // hw SPI buffer full (all 64 bytes filled, START THE TRANSSACTION
1018 host->hw->mosi_dlen.usr_mosi_dbitlen=bits-1; // Set mosi dbitlen
1019
1020 if ((duplex) && (rdcount > 0)) {
1021 // In full duplex mode we are receiving while sending !
1022 host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen
1023 host->hw->user.usr_miso = 1;
1024 }
1025 else {
1026 host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received
1027 host->hw->user.usr_miso = 0;
1028 }
1029
1030 // ** Start the transaction ***
1031 host->hw->cmd.usr=1;
1032 // Wait the transaction to finish
1033 while (host->hw->cmd.usr);
1034
1035 if ((duplex) && (rdcount > 0)) {
1036 // *** in full duplex mode transfer received data to input buffer ***
1037 rdidx = 0;
1038 while (bits > 0) {
1039 wd = host->hw->data_buf[rdidx];
1040 rdidx++;
1041 for (bc=0;bc<32;bc+=8) { // get max 4 bytes
1042 rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
1043 rdcount--;
1044 bits -= 8;
1045 if (rdcount == 0) {
1046 bits = 0;
1047 break; // Finished reading data
1048 }
1049 }
1050 }
1051 }
1052 bits = 0; // nothing in hw spi buffer yet
1053 idx = 0; // start from the beginning of the hw spi buffer
1054 }
1055 }
1056 // *** All transmit data are sent or pushed to hw spi buffer
1057 // bits > 0 IF THERE ARE SOME DATA STILL WAITING IN THE HW SPI TRANSMIT BUFFER
1058 if (bits > 0) {
1059 // ** WE HAVE SOME DATA IN THE HW SPI TRANSMIT BUFFER
1060 host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // Set mosi dbitlen
1061
1062 if ((duplex) && (rdcount > 0)) {
1063 // In full duplex mode we are receiving while sending !
1064 host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen
1065 host->hw->user.usr_miso = 1;
1066 }
1067 else {
1068 host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received
1069 host->hw->user.usr_miso = 0;
1070 }
1071
1072 // ** Start the transaction ***
1073 host->hw->cmd.usr=1;
1074 // Wait the transaction to finish
1075 while (host->hw->cmd.usr);
1076
1077 if ((duplex) && (rdcount > 0)) {
1078 // *** in full duplex mode transfer received data to input buffer ***
1079 rdidx = 0;
1080 while (bits > 0) {
1081 wd = host->hw->data_buf[rdidx];
1082 rdidx++;
1083 for (bc=0;bc<32;bc+=8) { // get max 4 bytes
1084 rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
1085 rdcount--;
1086 bits -= 8;
1087 if (bits == 0) break;
1088 if (rdcount == 0) {
1089 bits = 0;
1090 break; // Finished reading data
1091 }
1092 }
1093 }
1094 }
1095 }
1096 //if (duplex) rdcount = 0; // In duplex mode receive only as many bytes as was transmitted
1097 }
1098
1099 // ------------------------------------------------------------------------
1100 // *** If rdcount = 0 we have nothing to receive and we exit the function
1101 // This is true if no data receive was requested,
1102 // or all the data was received in Full duplex mode during the transmission
1103 // ------------------------------------------------------------------------
1104 if (rdcount > 0) {
1105 // ----------------------------------------------------------------------------------------------------------------
1106 // *** rdcount > 0, we have to receive some data
1107 // This is true if we operate in Half duplex mode when receiving after transmission is done,
1108 // or not all data was received in Full duplex mode during the transmission (trans->rxlength > trans->txlength)
1109 // ----------------------------------------------------------------------------------------------------------------
1110 host->hw->user.usr_mosi = 0; // do not send
1111 host->hw->user.usr_miso = 1; // do receive
1112 while (rdcount > 0) {
1113 if (rdcount <= 64) rdbits = rdcount * 8;
1114 else rdbits = 64 * 8;
1115
1116 // Load receive buffer
1117 host->hw->mosi_dlen.usr_mosi_dbitlen=0;
1118 host->hw->miso_dlen.usr_miso_dbitlen=rdbits-1;
1119
1120 // ** Start the transaction ***
1121 host->hw->cmd.usr=1;
1122 // Wait the transaction to finish
1123 while (host->hw->cmd.usr);
1124
1125 // *** transfer received data to input buffer ***
1126 rdidx = 0;
1127 while (rdbits > 0) {
1128 wd = host->hw->data_buf[rdidx];
1129 rdidx++;
1130 for (bc=0;bc<32;bc+=8) {
1131 rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
1132 rdcount--;
1133 rdbits -= 8;
1134 if (rdcount == 0) {
1135 rdbits = 0;
1136 break;
1137 }
1138 }
1139 }
1140 }
1141 }
1142
1143 // ** Call post-transmission callback, if any
1144 if (handle->cfg.post_cb) handle->cfg.post_cb(trans);
1145
1146 if (do_deselect) {
1147 // Spi device was selected in this function, we have to deselect it now
1148 ret = spi_lobo_device_deselect(handle);
1149 if (ret) return ret;
1150 }
1151
1152 return ESP_OK;
1153 }

mercurial