|
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 } |