1 /***************************************************************************//**
2 * \file cyhal_spi.c
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon SPI. This is
6 * a wrapper around the lower level PDL API.
7 *
8 ********************************************************************************
9 * \copyright
10 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
11 * an affiliate of Cypress Semiconductor Corporation
12 *
13 * SPDX-License-Identifier: Apache-2.0
14 *
15 * Licensed under the Apache License, Version 2.0 (the "License");
16 * you may not use this file except in compliance with the License.
17 * You may obtain a copy of the License at
18 *
19 * http://www.apache.org/licenses/LICENSE-2.0
20 *
21 * Unless required by applicable law or agreed to in writing, software
22 * distributed under the License is distributed on an "AS IS" BASIS,
23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 * See the License for the specific language governing permissions and
25 * limitations under the License.
26 *******************************************************************************/
27
28 /**
29 * \addtogroup group_hal_impl_spi SPI (Serial Peripheral Interface)
30 * \ingroup group_hal_impl
31 * \{
32 * \section section_hal_impl_spi_init_cfg Configurator-generated features limitations
33 * List of SPI personality items, which are currently not supported in SPI HAL driver on CAT1/CAT2 devices:
34 * - Modes: TI (Start Coincides), TI (Start Precedes), National Semiconductor (Microwire)
35 * - Low-level RX Interrupt sources
36 * - Low-level TX Interrupt sources
37 * - SPI Done Master interrupt source
38 * - Different data width for RX and TX
39 *
40 * \section section_hal_impl_spi_data_width Supported transfer data width options
41 * Next transfer data width options are supported by next devices:
42 * - CAT1 devices with MXSCB block version 2 and further (value of CY_IP_MXSCB_VERSION) support next data width range: 4 - 32 (included) with step 1
43 * - CAT1 devices with MXSCB block version 1 (value of CY_IP_MXSCB_VERSION) support next data width range: 4 - 16 (included) with step 1
44 * - CAT2 devices support next data width range : 4-16 (included) with step 1
45 *
46 * \} group_hal_impl_spi
47 */
48
49 #include <stdlib.h>
50 #include <string.h>
51 #include "cyhal_spi.h"
52 #include "cyhal_scb_common.h"
53 #include "cyhal_gpio.h"
54 #include "cyhal_system_impl.h"
55 #include "cyhal_hwmgr.h"
56 #include "cyhal_system.h"
57 #include "cyhal_syspm.h"
58 #include "cyhal_clock.h"
59 #include "cyhal_irq_impl.h"
60
61 #if (CYHAL_DRIVER_AVAILABLE_SPI)
62
63 #if defined(__cplusplus)
64 extern "C"
65 {
66 #endif
67
68 #define _CYHAL_SPI_DEFAULT_SPEED 100000
69
70 #define _CYHAL_SPI_WAIT_OP_RD_BUSY 1
71 #define _CYHAL_SPI_WAIT_OP_RD_NOT_BUSY 2
72 #define _CYHAL_SPI_WAIT_OP_WR 3
73 #define _CYHAL_SPI_WAIT_OP_VALID(op) ((op) > 0 && (op) <= _CYHAL_SPI_WAIT_OP_WR)
74
75 #define _CYHAL_SPI_OVERSAMPLE_MIN 4
76 #define _CYHAL_SPI_OVERSAMPLE_MAX 16
77 #define _CYHAL_SPI_SSEL_NUM 4
78
79 #define _CYHAL_SPI_PENDING_NONE 0
80 #define _CYHAL_SPI_PENDING_RX 1
81 #define _CYHAL_SPI_PENDING_TX 2
82 #define _CYHAL_SPI_PENDING_TX_RX 3
83
84 #define _CYHAL_SPI_SSEL_ACTIVATE true
85 #define _CYHAL_SPI_SSEL_DEACTIVATE false
86
87 // BWC: PDL finished new asymmetric transfer function in minor version 20
88 #if CY_SCB_DRV_VERSION_MINOR >= 20 && defined(COMPONENT_CAT1) && CY_SCB_DRV_VERSION_MAJOR == 3
89 #define _CYHAL_SPI_ASYMM_PDL_FUNC_AVAIL
90 #endif
91
92
93 /* Default SPI configuration */
94 static const cy_stc_scb_spi_config_t _cyhal_spi_default_config =
95 {
96 .spiMode = CY_SCB_SPI_MASTER,
97 .subMode = CY_SCB_SPI_MOTOROLA,
98 .sclkMode = CY_SCB_SPI_CPHA0_CPOL0,
99 #if defined (COMPONENT_CAT2) || (CY_IP_MXSCB_VERSION >= 3) || (CY_IP_MXS22SCB_VERSION >= 1)
100 .parity = CY_SCB_SPI_PARITY_NONE,
101 .dropOnParityError = false,
102 #endif /* defined (COMPONENT_CAT2) || (CY_IP_MXSCB_VERSION >= 3) */
103 .oversample = _CYHAL_SPI_OVERSAMPLE_MIN,
104 .rxDataWidth = 8,
105 .txDataWidth = 8,
106 .enableMsbFirst = true,
107 .enableFreeRunSclk = false,
108 .enableInputFilter = false,
109 #if (CY_IP_MXSCB_VERSION >= 3) || (CY_IP_MXS22SCB_VERSION >= 1)
110 /* Setting this to true leads to incorrect slave communication */
111 .enableMisoLateSample = false,
112 #else
113 .enableMisoLateSample = true,
114 #endif
115 .enableTransferSeperation = false,
116 .enableWakeFromSleep = false,
117 .ssPolarity = CY_SCB_SPI_ACTIVE_LOW,
118 #if defined (COMPONENT_CAT2) || (CY_IP_MXSCB_VERSION >= 3) || (CY_IP_MXS22SCB_VERSION >= 1)
119 .ssSetupDelay = false,
120 .ssHoldDelay = false,
121 #endif /* defined (COMPONENT_CAT2) || (CY_IP_MXSCB_VERSION >= 3) || (CY_IP_MXS22SCB_VERSION >= 1) */
122 #if defined (COMPONENT_CAT1) && ((CY_IP_MXSCB_VERSION >= 3) || (CY_IP_MXS22SCB_VERSION >= 1))
123 .ssInterFrameDelay = false,
124 #elif defined (COMPONENT_CAT2)
125 .ssInterDataframeDelay = false,
126 #endif /* defined (COMPONENT_CAT1) && ((CY_IP_MXSCB_VERSION >= 3) || (CY_IP_MXS22SCB_VERSION >= 1)) or defined (COMPONENT_CAT2) */
127 .rxFifoTriggerLevel = 0,
128 .rxFifoIntEnableMask = 0,
129 .txFifoTriggerLevel = 0,
130 .txFifoIntEnableMask = 0,
131 .masterSlaveIntEnableMask = 0
132 };
133
134 /* The PDL clears the IRQ status during Cy_SCB_SPI_Interrupt which prevents _cyhal_scb_get_irq_obj()
135 * from working properly in _cyhal_spi_cb_wrapper on devices with muxed IRQs, because they can't tell
136 * at that point which system IRQ caused the CPU IRQ. So we need to save this value at the beginning of the
137 * IRQ handler when we are able to determine what it is */
138 static volatile cyhal_spi_t* _cyhal_spi_irq_obj = NULL;
139
140 static void _cyhal_ssel_switch_state(cyhal_spi_t *obj, uint8_t ssel_idx, bool ssel_activate);
141
_cyhal_spi_int_frequency(cyhal_spi_t * obj,uint32_t hz,uint8_t * over_sample_val)142 static cy_rslt_t _cyhal_spi_int_frequency(cyhal_spi_t *obj, uint32_t hz, uint8_t *over_sample_val)
143 {
144 CY_ASSERT(NULL != obj);
145 cy_rslt_t result = CY_RSLT_SUCCESS;
146 uint8_t oversample_value;
147 uint32_t divider_value;
148 uint32_t last_diff = 0xFFFFFFFFU;
149 uint8_t last_ovrsmpl_val = 0;
150 uint32_t last_dvdr_val = 0;
151 uint32_t oversampled_freq = 0;
152 uint32_t divided_freq = 0;
153 uint32_t diff = 0;
154
155 uint32_t peri_freq = _cyhal_utils_get_peripheral_clock_frequency(&(obj->resource));
156 if (!obj->is_slave)
157 {
158 for (oversample_value = _CYHAL_SPI_OVERSAMPLE_MIN; oversample_value <= _CYHAL_SPI_OVERSAMPLE_MAX; oversample_value++)
159 {
160 oversampled_freq = hz * oversample_value;
161 if ((hz * oversample_value > peri_freq) && (_CYHAL_SPI_OVERSAMPLE_MIN == oversample_value))
162 {
163 return CYHAL_SPI_RSLT_CLOCK_ERROR;
164 }
165 else if (hz * oversample_value > peri_freq)
166 {
167 continue;
168 }
169
170 divider_value = _cyhal_utils_divider_value(&(obj->resource), hz * oversample_value, 0);
171 #if defined(COMPONENT_CAT5)
172 if(!_cyhal_clock_is_divider_valid(&(obj->resource), divider_value))
173 {
174 continue;
175 }
176 #endif
177 divided_freq = peri_freq / divider_value;
178 diff = (oversampled_freq > divided_freq)
179 ? oversampled_freq - divided_freq
180 : divided_freq - oversampled_freq;
181
182 if (diff < last_diff)
183 {
184 last_diff = diff;
185 last_ovrsmpl_val = oversample_value;
186 last_dvdr_val = divider_value;
187 if (0 == diff)
188 {
189 break;
190 }
191 }
192 }
193 *over_sample_val = last_ovrsmpl_val;
194 }
195 else
196 {
197 /* Slave requires such frequency: required_frequency = N / ((0.5 * desired_period) – 20 nsec - tDSI,
198 * N is 3 when "Enable Input Glitch Filter" is false and 4 when true.
199 * tDSI Is external master delay which is assumed to be 16.66 nsec */
200
201 /* Divided by 2 desired period to avoid dividing in required_frequency formula */
202 float desired_period_us_divided = 5e5f * (1 / (float)hz);
203 uint32_t required_frequency = (uint32_t)(3e6f / (desired_period_us_divided - 36.66f / 1e3f));
204
205 if (required_frequency > peri_freq)
206 {
207 return CYHAL_SPI_RSLT_CLOCK_ERROR;
208 }
209
210 /* Use maximum available clock for slave to make it able to work with any master environment */
211 #if defined(COMPONENT_CAT5)
212 /* On CAT5, frequency is set to (hz * oversample). Set to max oversample so the slave can match any master equal or slower
213 * than it. On other devices, last_dvdr_val is set to 1 to minimize the divider thus max the frequency, to the same effect. */
214 last_ovrsmpl_val = _CYHAL_SPI_OVERSAMPLE_MAX;
215 CY_UNUSED_PARAMETER(last_dvdr_val);
216 #else
217 last_dvdr_val = 1;
218 CY_UNUSED_PARAMETER(last_ovrsmpl_val);
219 #endif
220 }
221
222 #if defined (COMPONENT_CAT5)
223 if (last_ovrsmpl_val == 0)
224 {
225 result = CYHAL_SPI_RSLT_ERR_CFG_NOT_SUPPORTED;
226 }
227 else
228 {
229 _cyhal_utils_peri_pclk_disable_divider(_cyhal_scb_get_clock_index(obj->resource.block_num), &(obj->clock));
230 result = _cyhal_utils_peri_pclk_set_freq(_cyhal_scb_get_clock_index(obj->resource.block_num), &(obj->clock), hz, last_ovrsmpl_val);
231 if (CY_RSLT_SUCCESS == result)
232 {
233 _cyhal_utils_peri_pclk_enable_divider(_cyhal_scb_get_clock_index(obj->resource.block_num), &(obj->clock));
234 }
235 }
236 #else
237 result = cyhal_clock_set_enabled(&(obj->clock), false, false);
238 if (result == CY_RSLT_SUCCESS)
239 {
240 result = cyhal_clock_set_divider(&(obj->clock), last_dvdr_val);
241 }
242 if (result == CY_RSLT_SUCCESS)
243 {
244 result = cyhal_clock_set_enabled(&(obj->clock), true, false);
245 }
246 #endif
247
248 return result;
249 }
250
_cyhal_spi_convert_interrupt_cause(uint32_t pdl_cause)251 static inline cyhal_spi_event_t _cyhal_spi_convert_interrupt_cause(uint32_t pdl_cause)
252 {
253 static const uint32_t status_map[] =
254 {
255 (uint32_t)CYHAL_SPI_IRQ_ERROR, // Default error if unknown value is set
256 (uint32_t)CYHAL_SPI_IRQ_DATA_IN_FIFO, // CY_SCB_SPI_TRANSFER_IN_FIFO_EVENT
257 (uint32_t)CYHAL_SPI_IRQ_DONE, // CY_SCB_SPI_TRANSFER_CMPLT_EVENT
258 (uint32_t)CYHAL_SPI_IRQ_ERROR, // CY_SCB_SPI_TRANSFER_ERR_EVENT
259 };
260 return (cyhal_spi_event_t)_cyhal_utils_convert_flags(status_map, sizeof(status_map) / sizeof(uint32_t), pdl_cause);
261 }
262
263 #if defined (COMPONENT_CAT5)
_cyhal_spi_irq_handler(_cyhal_system_irq_t irqn)264 static void _cyhal_spi_irq_handler(_cyhal_system_irq_t irqn)
265 #else
266 static void _cyhal_spi_irq_handler(void)
267 #endif
268 {
269 /* Save the old value and store it aftewards in case we get into a nested IRQ situation */
270 /* Safe to cast away volatile because we don't expect this pointer to be changed while we're in here, they
271 * just might change where the original pointer points */
272 cyhal_spi_t* old_irq_obj = (cyhal_spi_t*)_cyhal_spi_irq_obj;
273 #if defined (COMPONENT_CAT5)
274 _cyhal_spi_irq_obj = (cyhal_spi_t*) _cyhal_scb_get_irq_obj(irqn);
275 #else
276 _cyhal_spi_irq_obj = (cyhal_spi_t*) _cyhal_scb_get_irq_obj();
277 #endif
278 cyhal_spi_t* obj = (cyhal_spi_t*)_cyhal_spi_irq_obj;
279
280 if (NULL == obj)
281 {
282 return;
283 }
284
285 Cy_SCB_SPI_Interrupt(obj->base, &(obj->context));
286
287 if (!obj->is_async)
288 {
289 return;
290 }
291
292 if (0 == (Cy_SCB_SPI_GetTransferStatus(obj->base, &obj->context) & CY_SCB_SPI_TRANSFER_ACTIVE))
293 {
294 #if !defined(_CYHAL_SPI_ASYMM_PDL_FUNC_AVAIL)
295 if (NULL != obj->tx_buffer)
296 {
297 /* Start TX Transfer */
298 obj->pending = _CYHAL_SPI_PENDING_TX;
299 const uint8_t *buf = obj->tx_buffer;
300 obj->tx_buffer = NULL;
301
302 Cy_SCB_SPI_Transfer(obj->base, (uint8_t *)buf, NULL, obj->tx_buffer_size, &obj->context);
303 }
304 else if (NULL != obj->rx_buffer)
305 {
306 /* Start RX Transfer */
307 obj->pending = _CYHAL_SPI_PENDING_RX;
308 uint8_t *rx_buf = obj->rx_buffer;
309 uint8_t *tx_buf;
310 size_t trx_size = obj->rx_buffer_size;
311
312 if (obj->rx_buffer_size > 1)
313 {
314 /* In this case we don't have a transmit buffer; we only have a receive buffer. While the PDL
315 * is fine with passing NULL for transmit, we don't get to control what data it is sending in
316 * that case, which we allowed the user to set. To honor the user's request, we reuse the rx
317 * buffer as the tx buffer too. We set all bytes beyond the one we will start filling in with
318 * the user provided 'write_fill'. This means the tx buffer is 1 element smaller than the rx
319 * buffer. As a result, we must therefore transfer 1 less element then we really want to in
320 * this transfer. When this transfer is complete, it will call into this again to receive the
321 * final element.
322 */
323 trx_size -= 1; // Transfer everything left except for the last byte
324
325 uint8_t **rx_buffer_p = (uint8_t **) &obj->rx_buffer;
326
327 tx_buf = *rx_buffer_p + 1; // Start at second byte to avoid trying to transmit and receive the same byte
328 memset(tx_buf, obj->write_fill, trx_size);
329
330 *rx_buffer_p += trx_size; // Move to 1 byte before end
331 obj->rx_buffer_size = 1; // Transfer the last byte on the next interrupt
332 }
333 else
334 {
335 tx_buf = &obj->write_fill;
336
337 obj->rx_buffer = NULL;
338 }
339
340 Cy_SCB_SPI_Transfer(obj->base, tx_buf, rx_buf, trx_size, &obj->context);
341 }
342 else
343 #endif /* #if !defined(_CYHAL_SPI_ASYMM_PDL_FUNC_AVAIL) */
344 {
345 /* Finish Async Transfer */
346 obj->pending = _CYHAL_SPI_PENDING_NONE;
347 obj->is_async = false;
348 _cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_DEACTIVATE);
349 }
350 }
351
352 _cyhal_spi_irq_obj = old_irq_obj;
353 }
354
355 #if defined (COMPONENT_CAT5)
_cyhal_spi0_irq_handler(void)356 static void _cyhal_spi0_irq_handler(void)
357 {
358 _cyhal_spi_irq_handler(scb_0_interrupt_IRQn);
359 Cy_SCB_EnableInterrupt(SCB0);
360 }
361
_cyhal_spi1_irq_handler(void)362 static void _cyhal_spi1_irq_handler(void)
363 {
364 _cyhal_spi_irq_handler(scb_1_interrupt_IRQn);
365 Cy_SCB_EnableInterrupt(SCB1);
366 }
367
_cyhal_spi2_irq_handler(void)368 static void _cyhal_spi2_irq_handler(void)
369 {
370 _cyhal_spi_irq_handler(scb_2_interrupt_IRQn);
371 Cy_SCB_EnableInterrupt(SCB2);
372 }
373
374 static CY_SCB_IRQ_THREAD_CB_t _cyhal_irq_cb[3] = {_cyhal_spi0_irq_handler, _cyhal_spi1_irq_handler, _cyhal_spi2_irq_handler};
375 #endif
376
_cyhal_spi_cb_wrapper(uint32_t event)377 static void _cyhal_spi_cb_wrapper(uint32_t event)
378 {
379 /* Safe to cast away volatile because we don't expect this pointer to be changed while we're in here, they
380 * just might change where the original pointer points */
381 cyhal_spi_t *obj = (cyhal_spi_t*)_cyhal_spi_irq_obj;
382 cyhal_spi_event_t anded_events = (cyhal_spi_event_t) (obj->irq_cause & (uint32_t) _cyhal_spi_convert_interrupt_cause(event));
383
384 // Don't call the callback until the final transfer has put everything in the FIFO/completed
385 if ((anded_events & (CYHAL_SPI_IRQ_DATA_IN_FIFO | CYHAL_SPI_IRQ_DONE)) && !(obj->rx_buffer == NULL && obj->tx_buffer == NULL))
386 {
387 return;
388 }
389
390 if (anded_events)
391 {
392 /* Indicates read/write operations will be in a callback */
393 obj->op_in_callback = true;
394 cyhal_spi_event_callback_t callback = (cyhal_spi_event_callback_t) obj->callback_data.callback;
395 callback(obj->callback_data.callback_arg, anded_events);
396 obj->op_in_callback = false;
397 }
398 }
399
_cyhal_convert_mode_sclk(cyhal_spi_mode_t mode)400 static cy_en_scb_spi_sclk_mode_t _cyhal_convert_mode_sclk(cyhal_spi_mode_t mode)
401 {
402 uint8_t sclk_mode = (mode & (CYHAL_SPI_MODE_FLAG_CPOL | CYHAL_SPI_MODE_FLAG_CPHA));
403
404 switch (sclk_mode)
405 {
406 case CYHAL_SPI_MODE_FLAG_CPOL | CYHAL_SPI_MODE_FLAG_CPHA:
407 return (CY_SCB_SPI_CPHA1_CPOL1);
408 case CYHAL_SPI_MODE_FLAG_CPOL:
409 return (CY_SCB_SPI_CPHA0_CPOL1);
410 case CYHAL_SPI_MODE_FLAG_CPHA:
411 return (CY_SCB_SPI_CPHA1_CPOL0);
412 default:
413 return (CY_SCB_SPI_CPHA0_CPOL0);
414 }
415 }
416
_is_cyhal_mode_msb(cyhal_spi_mode_t mode)417 static inline bool _is_cyhal_mode_msb(cyhal_spi_mode_t mode)
418 {
419 return ((mode & CYHAL_SPI_MODE_FLAG_LSB) != CYHAL_SPI_MODE_FLAG_LSB);
420 }
421
_cyhal_spi_mode_pdl_to_hal(const cy_stc_scb_spi_config_t * pdl_cfg)422 static cyhal_spi_mode_t _cyhal_spi_mode_pdl_to_hal(const cy_stc_scb_spi_config_t *pdl_cfg)
423 {
424 cy_en_scb_spi_sclk_mode_t clk_mode = pdl_cfg->sclkMode;
425 return (cyhal_spi_mode_t)(CYHAL_SPI_MODE(
426 ((uint8_t)((clk_mode == CY_SCB_SPI_CPHA0_CPOL1) || (clk_mode == CY_SCB_SPI_CPHA1_CPOL1))),
427 ((uint8_t)((clk_mode == CY_SCB_SPI_CPHA1_CPOL0) || (clk_mode == CY_SCB_SPI_CPHA1_CPOL1))),
428 ((uint8_t) !pdl_cfg->enableMsbFirst)));
429 }
430
_cyhal_spi_pm_callback_instance(void * obj_ptr,cyhal_syspm_callback_state_t state,cy_en_syspm_callback_mode_t pdl_mode)431 static bool _cyhal_spi_pm_callback_instance(void *obj_ptr, cyhal_syspm_callback_state_t state, cy_en_syspm_callback_mode_t pdl_mode)
432 {
433 cyhal_spi_t *obj = (cyhal_spi_t *)obj_ptr;
434 bool allow = true;
435 cy_stc_syspm_callback_params_t spi_callback_params = {
436 .base = (void *) (obj->base),
437 .context = (void *) &(obj->context)
438 };
439
440 if ((CYHAL_SYSPM_CB_CPU_DEEPSLEEP == state) || (CYHAL_SYSPM_CB_CPU_DEEPSLEEP_RAM == state))
441 allow = (CY_SYSPM_SUCCESS == Cy_SCB_SPI_DeepSleepCallback(&spi_callback_params, pdl_mode));
442 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1D)
443 else if (CYHAL_SYSPM_CB_SYSTEM_HIBERNATE == state)
444 allow = (CY_SYSPM_SUCCESS == Cy_SCB_SPI_HibernateCallback(&spi_callback_params, pdl_mode));
445 #endif
446
447 return allow;
448 }
449
_cyhal_spi_get_ssel_map_idx(cyhal_gpio_t ssel,const cyhal_resource_pin_mapping_t ** ssel_map,uint8_t * idx,const cyhal_resource_inst_t * target_blk)450 static cy_rslt_t _cyhal_spi_get_ssel_map_idx(cyhal_gpio_t ssel, const cyhal_resource_pin_mapping_t **ssel_map,
451 uint8_t *idx, const cyhal_resource_inst_t *target_blk)
452 {
453 static const cyhal_resource_pin_mapping_t *ssel_s_pin_maps[] = {
454 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT0)
455 cyhal_pin_map_scb_spi_s_select0,
456 #endif
457 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT1)
458 cyhal_pin_map_scb_spi_s_select1,
459 #endif
460 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT2)
461 cyhal_pin_map_scb_spi_s_select2,
462 #endif
463 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT3)
464 cyhal_pin_map_scb_spi_s_select3
465 #endif
466 };
467 static const size_t ssel_s_pin_maps_sizes_bytes[] = {
468 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT0)
469 sizeof(cyhal_pin_map_scb_spi_s_select0),
470 #endif
471 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT1)
472 sizeof(cyhal_pin_map_scb_spi_s_select1),
473 #endif
474 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT2)
475 sizeof(cyhal_pin_map_scb_spi_s_select2),
476 #endif
477 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT3)
478 sizeof(cyhal_pin_map_scb_spi_s_select3)
479 #endif
480 };
481
482 for (uint8_t i = 0; i < sizeof(ssel_s_pin_maps) / sizeof(ssel_s_pin_maps[0]); i++)
483 {
484 *ssel_map = _cyhal_scb_find_map(ssel, ssel_s_pin_maps[i],
485 ssel_s_pin_maps_sizes_bytes[i] / sizeof(cyhal_resource_pin_mapping_t), target_blk);
486 if (NULL != *ssel_map)
487 {
488 *idx = i;
489 return CY_RSLT_SUCCESS;
490 }
491 }
492 return CYHAL_SPI_RSLT_ERR_CANNOT_CONFIG_SSEL;
493 }
494
_cyhal_spi_pol_from_hal_to_pdl(cyhal_spi_ssel_polarity_t hal_polarity)495 static inline cy_en_scb_spi_polarity_t _cyhal_spi_pol_from_hal_to_pdl(cyhal_spi_ssel_polarity_t hal_polarity)
496 {
497 return (hal_polarity == CYHAL_SPI_SSEL_ACTIVE_HIGH) ? CY_SCB_SPI_ACTIVE_HIGH : CY_SCB_SPI_ACTIVE_LOW;
498 }
499
_cyhal_spi_ssel_config(cyhal_spi_t * obj,cyhal_gpio_t ssel,cyhal_spi_ssel_polarity_t polarity,bool reserve_n_connect)500 static cy_rslt_t _cyhal_spi_ssel_config(cyhal_spi_t *obj, cyhal_gpio_t ssel,
501 cyhal_spi_ssel_polarity_t polarity, bool reserve_n_connect)
502 {
503 cy_rslt_t result = CYHAL_SPI_RSLT_ERR_CANNOT_CONFIG_SSEL;
504 uint8_t found_idx = 0;
505 bool configuring_existing = false;
506 if ((NC != ssel) && (_CYHAL_SPI_PENDING_NONE == obj->pending))
507 {
508 for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
509 {
510 if ((configuring_existing = (ssel == obj->pin_ssel[i])))
511 {
512 result = CY_RSLT_SUCCESS;
513 found_idx = i;
514 break;
515 }
516 if (!obj->is_slave)
517 {
518 /* Looking for first available ssel slot */
519 if ((NC == obj->pin_ssel[i]))
520 {
521 result = reserve_n_connect ? cyhal_gpio_init(ssel, CYHAL_GPIO_DIR_OUTPUT, CYHAL_GPIO_DRIVE_STRONG,
522 (polarity == CYHAL_SPI_SSEL_ACTIVE_LOW) ? true : false) : CY_RSLT_SUCCESS;
523 found_idx = i;
524 break;
525 }
526 }
527 }
528 if (!configuring_existing && (obj->is_slave))
529 {
530 const cyhal_resource_pin_mapping_t *ssel_map = NULL;
531 if (CY_RSLT_SUCCESS == _cyhal_spi_get_ssel_map_idx(ssel, &ssel_map, &found_idx, &(obj->resource)))
532 {
533 /* Ensure the block_num and channel_num for ssel matches the block_num and channel_num of the SPI resource */
534 if ((NULL != ssel_map) && (NC == obj->pin_ssel[found_idx]) &&
535 (ssel_map->block_num == obj->resource.block_num) &&
536 (ssel_map->channel_num == obj->resource.channel_num))
537 {
538 result = reserve_n_connect
539 ? _cyhal_utils_reserve_and_connect(ssel_map, (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT0)
540 : CY_RSLT_SUCCESS;
541 }
542 }
543 }
544 if (CY_RSLT_SUCCESS == result)
545 {
546 if (!configuring_existing)
547 obj->pin_ssel[found_idx] = ssel;
548 obj->ssel_pol[found_idx] = _cyhal_spi_pol_from_hal_to_pdl(polarity);
549
550 /* Immediately apply updated slave select polarity */
551 Cy_SCB_SPI_SetActiveSlaveSelectPolarity(obj->base, (cy_en_scb_spi_slave_select_t)found_idx, obj->ssel_pol[found_idx]);
552 if (!obj->is_slave)
553 {
554 _cyhal_ssel_switch_state(obj, found_idx, _CYHAL_SPI_SSEL_DEACTIVATE);
555 }
556 }
557 }
558 return result;
559 }
560
561 /* Wait untill SPI is busy for some time (timeout > 0) or no wait (timeout == 0). */
_cyhal_spi_wait_for_op(cyhal_spi_t * obj,uint8_t op,uint32_t * timeout)562 static cy_rslt_t _cyhal_spi_wait_for_op(cyhal_spi_t *obj, uint8_t op, uint32_t* timeout)
563 {
564 CY_ASSERT(_CYHAL_SPI_WAIT_OP_VALID(op));
565
566 cy_rslt_t result = CY_RSLT_SUCCESS;
567 uint32_t timeout_us = _CYHAL_UTILS_US_PER_MS;
568 bool op_condition = true;
569
570 if (!(obj->op_in_callback) && *timeout > 0)
571 {
572 while (op_condition && *timeout > 0)
573 {
574 switch (op)
575 {
576 case _CYHAL_SPI_WAIT_OP_RD_BUSY:
577 op_condition = (cyhal_spi_readable(obj) == 0 && !cyhal_spi_is_busy(obj));
578 break;
579 case _CYHAL_SPI_WAIT_OP_RD_NOT_BUSY:
580 op_condition = (cyhal_spi_readable(obj) == 0 || cyhal_spi_is_busy(obj));
581 break;
582 case _CYHAL_SPI_WAIT_OP_WR:
583 op_condition = ((cyhal_spi_writable(obj) < Cy_SCB_GetFifoSize(obj->base)) || cyhal_spi_is_busy(obj));
584 break;
585 default:
586 op_condition = false;
587 break;
588 }
589
590 if (timeout_us > 0)
591 {
592 cyhal_system_delay_us(_CYHAL_UTILS_ONE_TIME_UNIT);
593 --timeout_us;
594 }
595 else
596 {
597 timeout_us = _CYHAL_UTILS_US_PER_MS;
598 (*timeout)--;
599 }
600 }
601 result = (*timeout > 0) ? CY_RSLT_SUCCESS : CYHAL_SPI_RSLT_WARN_TIMEOUT;
602 }
603 return result;
604 }
605
_cyhal_spi_setup_resources(cyhal_spi_t * obj,cyhal_gpio_t mosi,cyhal_gpio_t miso,cyhal_gpio_t sclk,cyhal_gpio_t ssel,const cyhal_clock_t * clk,uint32_t ssPolarity)606 static cy_rslt_t _cyhal_spi_setup_resources(cyhal_spi_t *obj, cyhal_gpio_t mosi, cyhal_gpio_t miso, cyhal_gpio_t sclk, cyhal_gpio_t ssel,
607 const cyhal_clock_t *clk, uint32_t ssPolarity)
608 {
609 cy_rslt_t result = CY_RSLT_SUCCESS;
610
611 obj->resource.type = CYHAL_RSC_INVALID;
612 obj->pending = _CYHAL_SPI_PENDING_NONE;
613 obj->write_fill = (uint8_t) CY_SCB_SPI_DEFAULT_TX;
614 obj->oversample_value = _CYHAL_SPI_OVERSAMPLE_MIN;
615
616 obj->pin_mosi = NC;
617 obj->pin_miso = NC;
618 obj->pin_sclk = NC;
619
620 uint32_t saved_intr_status = cyhal_system_critical_section_enter();
621
622 // pins_blocks will contain bit representation of blocks, that are connected to specified pin
623 // 1 block - 1 bit, so, for example, pin_blocks = 0x00000006 means that certain pin
624 // can belong to next non-reserved blocks SCB2 and SCB1
625 uint32_t pins_blocks = _CYHAL_SCB_AVAILABLE_BLOCKS_MASK;
626 uint8_t found_block_idx = 0;
627 if (obj->is_slave)
628 {
629 if (NC != sclk)
630 {
631 pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(sclk, cyhal_pin_map_scb_spi_s_clk);
632 }
633 if (NC != miso)
634 {
635 pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(miso, cyhal_pin_map_scb_spi_s_miso);
636 }
637 if (NC != mosi)
638 {
639 pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(mosi, cyhal_pin_map_scb_spi_s_mosi);
640 }
641 if (NC != ssel)
642 {
643 uint32_t ss_pins_blocks = 0;
644
645 #ifdef CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT0
646 ss_pins_blocks |= _CYHAL_SCB_CHECK_AFFILIATION(ssel, cyhal_pin_map_scb_spi_s_select0);
647 #endif /* (CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT0) */
648
649 #ifdef CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT1
650 ss_pins_blocks |= _CYHAL_SCB_CHECK_AFFILIATION(ssel, cyhal_pin_map_scb_spi_s_select1);
651 #endif /* (CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT1) */
652
653 #ifdef CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT2
654 ss_pins_blocks |= _CYHAL_SCB_CHECK_AFFILIATION(ssel, cyhal_pin_map_scb_spi_s_select2);
655 #endif /* (CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT2) */
656
657 #ifdef CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT3
658 ss_pins_blocks |= _CYHAL_SCB_CHECK_AFFILIATION(ssel, cyhal_pin_map_scb_spi_s_select3);
659 #endif /* (CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_SELECT3) */
660
661 pins_blocks &= ss_pins_blocks;
662 }
663 }
664 else
665 {
666 if (NC != sclk)
667 {
668 pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(sclk, cyhal_pin_map_scb_spi_m_clk);
669 }
670 if (NC != miso)
671 {
672 pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(miso, cyhal_pin_map_scb_spi_m_miso);
673 }
674 if (NC != mosi)
675 {
676 pins_blocks &= _CYHAL_SCB_CHECK_AFFILIATION(mosi, cyhal_pin_map_scb_spi_m_mosi);
677 }
678 /* No need to check SSEL for SPI master as the SS pin can be any */
679 }
680
681 if (0 == pins_blocks)
682 {
683 // One (or more) pin does not belong to any SCB instance or all corresponding SCB instances
684 // are reserved
685 result = CYHAL_SPI_RSLT_ERR_INVALID_PIN;
686 }
687 else
688 {
689 while(((pins_blocks >> found_block_idx) & 0x1) == 0)
690 {
691 found_block_idx++;
692 }
693 }
694
695 /* Get pin configurations */
696 const cyhal_resource_pin_mapping_t *mosi_map = NULL;
697 const cyhal_resource_pin_mapping_t *miso_map = NULL;
698 const cyhal_resource_pin_mapping_t *sclk_map = NULL;
699 const cyhal_resource_pin_mapping_t *ssel_map = NULL;
700 uint8_t mosi_dm = 0, miso_dm = 0, sclk_dm = 0;
701
702 const cyhal_resource_inst_t default_spi_inst = { CYHAL_RSC_SCB, found_block_idx, 0 };
703 const cyhal_resource_inst_t* spi_inst_p = &default_spi_inst;
704
705 if (CY_RSLT_SUCCESS == result)
706 {
707 if (obj->is_slave)
708 {
709 mosi_map = _CYHAL_SCB_FIND_MAP_BLOCK(mosi, cyhal_pin_map_scb_spi_s_mosi, spi_inst_p);
710 miso_map = _CYHAL_SCB_FIND_MAP_BLOCK(miso, cyhal_pin_map_scb_spi_s_miso, spi_inst_p);
711 sclk_map = _CYHAL_SCB_FIND_MAP_BLOCK(sclk, cyhal_pin_map_scb_spi_s_clk, spi_inst_p);
712 mosi_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_MOSI;
713 miso_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_MISO;
714 sclk_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_S_CLK;
715 result = _cyhal_spi_get_ssel_map_idx(ssel, &ssel_map, &obj->active_ssel, spi_inst_p);
716 }
717 else
718 {
719 mosi_map = _CYHAL_SCB_FIND_MAP_BLOCK(mosi, cyhal_pin_map_scb_spi_m_mosi, spi_inst_p);
720 miso_map = _CYHAL_SCB_FIND_MAP_BLOCK(miso, cyhal_pin_map_scb_spi_m_miso, spi_inst_p);
721 sclk_map = _CYHAL_SCB_FIND_MAP_BLOCK(sclk, cyhal_pin_map_scb_spi_m_clk, spi_inst_p);
722 mosi_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_M_MOSI;
723 miso_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_M_MISO;
724 sclk_dm = (uint8_t)CYHAL_PIN_MAP_DRIVE_MODE_SCB_SPI_M_CLK;
725 /* No need to find maps for ssel pins, as GPIO used */
726 }
727
728 const cyhal_resource_pin_mapping_t *base_map = (NC != mosi)
729 ? (mosi_map != NULL ? mosi_map : NULL)
730 : (miso_map != NULL ? miso_map : NULL);
731
732 /* Validate pins mapping */
733 if((NC != mosi) || (NC != miso))
734 {
735 if (NULL == base_map ||
736 ((NC != mosi) && ((NULL == mosi_map) || !_cyhal_utils_map_resources_equal(base_map, mosi_map))) ||
737 ((NC != miso) && ((NULL == miso_map) || !_cyhal_utils_map_resources_equal(base_map, miso_map))) ||
738 ((NC != sclk) && ((NULL == sclk_map) || !_cyhal_utils_map_resources_equal(base_map, sclk_map))) ||
739 ((obj->is_slave) && ((NC != ssel) && ((NULL == ssel_map) || !_cyhal_utils_map_resources_equal(base_map, ssel_map)))))
740 {
741 result = CYHAL_SPI_RSLT_ERR_INVALID_PIN;
742 }
743 }
744 }
745
746 if (CY_RSLT_SUCCESS == result)
747 {
748 if (false == obj->dc_configured)
749 {
750 cyhal_resource_inst_t rsc_to_reserve = { CYHAL_RSC_SCB, _cyhal_scb_get_block_index(spi_inst_p->block_num), 0 };
751 result = cyhal_hwmgr_reserve(&rsc_to_reserve);
752 }
753 }
754
755 cyhal_system_critical_section_exit(saved_intr_status);
756
757 if (CY_RSLT_SUCCESS == result)
758 {
759 obj->resource = *spi_inst_p;
760 uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
761 obj->base = _CYHAL_SCB_BASE_ADDRESSES[scb_arr_index];
762 #if defined(COMPONENT_CAT5)
763 // Check if either clock hasn't been provided thus not initialized (clock == NULL), or the user has not yet
764 // set the clock frequency (which enables it).
765 if(clk == NULL)
766 {
767 result = _cyhal_utils_allocate_clock(&(obj->clock), &(obj->resource), CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
768 obj->alloc_clock = true;
769 result = _cyhal_spi_int_frequency(obj, _CYHAL_SPI_DEFAULT_SPEED, &obj->oversample_value);
770 }
771 if((clk != NULL) && (_cyhal_utils_peri_pclk_get_freq(0, clk) == 0))
772 {
773 obj->clock = *clk;
774 obj->alloc_clock = false;
775 result = _cyhal_spi_int_frequency(obj, _CYHAL_SPI_DEFAULT_SPEED, &obj->oversample_value);
776 }
777 #endif
778 }
779
780 // reserve the MOSI pin
781 if (result == CY_RSLT_SUCCESS)
782 {
783 if (NC != mosi)
784 {
785 if (false == obj->dc_configured)
786 {
787 result = _cyhal_utils_reserve_and_connect(mosi_map, mosi_dm);
788 }
789 if (result == CY_RSLT_SUCCESS)
790 {
791 obj->pin_mosi = mosi;
792 }
793 }
794 }
795
796 // reserve the MISO pin
797 if (result == CY_RSLT_SUCCESS)
798 {
799 if (NC != miso)
800 {
801 if (false == obj->dc_configured)
802 {
803 result = _cyhal_utils_reserve_and_connect(miso_map, miso_dm);
804 }
805 if (result == CY_RSLT_SUCCESS)
806 {
807 obj->pin_miso = miso;
808 }
809 }
810 }
811
812 // reserve the SCLK pin
813 if (result == CY_RSLT_SUCCESS)
814 {
815 if (NC != sclk)
816 {
817 if (false == obj->dc_configured)
818 {
819 result = _cyhal_utils_reserve_and_connect(sclk_map, sclk_dm);
820 }
821 if (result == CY_RSLT_SUCCESS)
822 {
823 obj->pin_sclk = sclk;
824 }
825 }
826 }
827
828 // reserve and configure the SSEL pin
829 if ((result == CY_RSLT_SUCCESS) && (NC != ssel))
830 {
831 result = _cyhal_spi_ssel_config(obj, ssel,
832 (cyhal_spi_ssel_polarity_t)((ssPolarity >> obj->active_ssel) & 0x1), !obj->dc_configured);
833 }
834
835 #if !defined(COMPONENT_CAT5)
836 // Already handled above for CAT5
837 if (result == CY_RSLT_SUCCESS)
838 {
839 if (clk == NULL)
840 {
841 result = _cyhal_utils_allocate_clock(&(obj->clock), &(obj->resource), CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
842 obj->alloc_clock = true;
843 }
844 else
845 {
846 obj->clock = *clk;
847 obj->alloc_clock = false;
848
849 /* Per CDT 315848 and 002-20730 Rev. *E:
850 * For SPI, an integer clock divider must be used for both master and slave. */
851 if ((_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(obj->clock.block) == (cy_en_divider_types_t)CYHAL_CLOCK_BLOCK_PERIPHERAL_16_5BIT) ||
852 (_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(obj->clock.block) == (cy_en_divider_types_t)CYHAL_CLOCK_BLOCK_PERIPHERAL_24_5BIT))
853 {
854 result = CYHAL_SPI_RSLT_CLOCK_NOT_SUPPORTED;
855 }
856 }
857 }
858 #endif
859 if (result == CY_RSLT_SUCCESS)
860 {
861 result = _cyhal_utils_peri_pclk_assign_divider(_cyhal_scb_get_clock_index(obj->resource.block_num), &(obj->clock));
862
863 if ((result == CY_RSLT_SUCCESS) && obj->alloc_clock)
864 {
865 result = _cyhal_spi_int_frequency(obj, _CYHAL_SPI_DEFAULT_SPEED, &obj->oversample_value);
866 }
867 }
868
869 return result;
870 }
871
_cyhal_spi_init_hw(cyhal_spi_t * obj,cy_stc_scb_spi_config_t * cfg)872 static cy_rslt_t _cyhal_spi_init_hw(cyhal_spi_t *obj, cy_stc_scb_spi_config_t *cfg)
873 {
874 cy_rslt_t result = CY_RSLT_SUCCESS;
875
876 _cyhal_scb_update_instance_data(obj->resource.block_num, (void*)obj, &_cyhal_spi_pm_callback_instance);
877 if ((false != obj->dc_configured) || !(obj->alloc_clock))
878 {
879 obj->oversample_value = cfg->oversample;
880 }
881 obj->data_bits = cfg->txDataWidth;
882 obj->msb_first = cfg->enableMsbFirst;
883 obj->clk_mode = cfg->sclkMode;
884 obj->mode = (uint8_t) _cyhal_spi_mode_pdl_to_hal(cfg);
885
886 result = (cy_rslt_t)Cy_SCB_SPI_Init(obj->base, cfg, &(obj->context));
887
888 if (result == CY_RSLT_SUCCESS)
889 {
890 /* Activating specified by user ssel after init */
891 if (NC != obj->pin_ssel[obj->active_ssel])
892 {
893 result = cyhal_spi_select_active_ssel(obj, obj->pin_ssel[obj->active_ssel]);
894 }
895 }
896
897 if (result == CY_RSLT_SUCCESS)
898 {
899 uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
900
901 obj->callback_data.callback = NULL;
902 obj->callback_data.callback_arg = NULL;
903 obj->irq_cause = 0;
904
905 #if defined (COMPONENT_CAT5)
906 Cy_SCB_RegisterInterruptCallback(obj->base, _cyhal_irq_cb[obj->resource.block_num]);
907 Cy_SCB_EnableInterrupt(obj->base);
908 #endif
909
910 _cyhal_irq_register(_CYHAL_SCB_IRQ_N[scb_arr_index], CYHAL_ISR_PRIORITY_DEFAULT, (cy_israddress)_cyhal_spi_irq_handler );
911 _cyhal_irq_enable(_CYHAL_SCB_IRQ_N[scb_arr_index]);
912 Cy_SCB_SPI_Enable(obj->base);
913 }
914 else
915 {
916 cyhal_spi_free(obj);
917 }
918 return result;
919 }
920
cyhal_spi_init(cyhal_spi_t * obj,cyhal_gpio_t mosi,cyhal_gpio_t miso,cyhal_gpio_t sclk,cyhal_gpio_t ssel,const cyhal_clock_t * clk,uint8_t bits,cyhal_spi_mode_t mode,bool is_slave)921 cy_rslt_t cyhal_spi_init(cyhal_spi_t *obj, cyhal_gpio_t mosi, cyhal_gpio_t miso, cyhal_gpio_t sclk, cyhal_gpio_t ssel,
922 const cyhal_clock_t *clk, uint8_t bits, cyhal_spi_mode_t mode, bool is_slave)
923 {
924 CY_ASSERT(NULL != obj);
925 memset(obj, 0, sizeof(cyhal_spi_t));
926
927 cy_stc_scb_spi_config_t driver_config = _cyhal_spi_default_config;
928 driver_config.spiMode = is_slave == 0
929 ? CY_SCB_SPI_MASTER
930 : CY_SCB_SPI_SLAVE;
931 driver_config.enableMsbFirst = _is_cyhal_mode_msb(mode);
932 driver_config.sclkMode = _cyhal_convert_mode_sclk(mode);
933 driver_config.rxDataWidth = bits;
934 driver_config.txDataWidth = bits;
935 driver_config.ssPolarity = ((CY_SCB_SPI_ACTIVE_LOW << CY_SCB_SPI_SLAVE_SELECT0) | \
936 (CY_SCB_SPI_ACTIVE_LOW << CY_SCB_SPI_SLAVE_SELECT1) | \
937 (CY_SCB_SPI_ACTIVE_LOW << CY_SCB_SPI_SLAVE_SELECT2) | \
938 (CY_SCB_SPI_ACTIVE_LOW << CY_SCB_SPI_SLAVE_SELECT3));
939
940 cy_rslt_t result = CY_RSLT_SUCCESS;
941
942 // Explicitly marked not allocated resources as invalid to prevent freeing them.
943 for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
944 {
945 obj->pin_ssel[i] = NC;
946 obj->ssel_pol[i] = CY_SCB_SPI_ACTIVE_LOW;
947 }
948
949 obj->active_ssel = 0;
950 obj->is_slave = is_slave;
951
952 result = _cyhal_spi_setup_resources(obj, mosi, miso, sclk, ssel, clk, driver_config.ssPolarity);
953
954 if (result == CY_RSLT_SUCCESS)
955 {
956 result = _cyhal_spi_init_hw(obj, &driver_config);
957 }
958 else
959 {
960 cyhal_spi_free(obj);
961 }
962 return result;
963 }
964
cyhal_spi_init_cfg(cyhal_spi_t * obj,const cyhal_spi_configurator_t * cfg)965 cy_rslt_t cyhal_spi_init_cfg(cyhal_spi_t *obj, const cyhal_spi_configurator_t *cfg)
966 {
967 CY_ASSERT(NULL != obj);
968 CY_ASSERT(NULL != cfg);
969 CY_ASSERT(NULL != cfg->config);
970
971 memset(obj, 0, sizeof(cyhal_spi_t));
972
973 obj->dc_configured = (NULL != cfg->resource);
974 cy_stc_scb_spi_config_t cfg_local = *cfg->config;
975
976 if (obj->dc_configured &&
977 ((cfg_local.subMode != CY_SCB_SPI_MOTOROLA) ||
978 (cfg_local.rxFifoIntEnableMask != 0) ||
979 (cfg_local.txFifoIntEnableMask != 0) ||
980 (cfg_local.masterSlaveIntEnableMask != 0) ||
981 (cfg_local.rxDataWidth != cfg_local.txDataWidth)))
982 {
983 /* Next SPI personality (device configurator) items are NOT supported by SPI HAL:
984 - Modes: TI (Start Coincides), TI (Start Precedes), National Semiconductor (Microwire)
985 - Low-level RX Interrupt sources
986 - Low-level TX Interrupt sources
987 - SPI Done Master interrupt source
988 - Different data width for RX and TX
989 */
990 return CYHAL_SPI_RSLT_ERR_CFG_NOT_SUPPORTED;
991 }
992
993 cy_rslt_t result = CY_RSLT_SUCCESS;
994 bool is_slave = (cfg_local.spiMode == CY_SCB_SPI_SLAVE);
995
996 // Explicitly marked not allocated resources as invalid to prevent freeing them.
997 for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
998 {
999 obj->pin_ssel[i] = NC;
1000 obj->ssel_pol[i] = CY_SCB_SPI_ACTIVE_LOW;
1001 }
1002
1003 obj->resource = *cfg->resource;
1004 uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
1005 obj->base = _CYHAL_SCB_BASE_ADDRESSES[scb_arr_index];
1006
1007 obj->active_ssel = 0;
1008 obj->is_slave = is_slave;
1009 obj->write_fill = (uint8_t) CY_SCB_SPI_DEFAULT_TX;
1010
1011 //Copy pins from configurator object
1012 obj->pin_mosi = cfg->gpios.mosi;
1013 obj->pin_miso = cfg->gpios.miso;
1014 obj->pin_sclk = cfg->gpios.sclk;
1015 for (size_t ssel_idx = 0; ssel_idx < sizeof(cfg->gpios.ssel) / sizeof(cfg->gpios.ssel[0]); ++ssel_idx)
1016 {
1017 if (NC != cfg->gpios.ssel[ssel_idx])
1018 {
1019 obj->pin_ssel[ssel_idx] = cfg->gpios.ssel[ssel_idx];
1020 obj->active_ssel = ssel_idx;
1021 result = _cyhal_spi_ssel_config(obj, obj->pin_ssel[ssel_idx],
1022 (cyhal_spi_ssel_polarity_t)((obj->ssel_pol[ssel_idx] >> obj->active_ssel) & 0x1), !obj->dc_configured);
1023 break;
1024 }
1025 }
1026
1027
1028 #if !defined(COMPONENT_CAT5)
1029 if (result == CY_RSLT_SUCCESS)
1030 {
1031 if (cfg->clock == NULL)
1032 {
1033 result = _cyhal_utils_allocate_clock(&(obj->clock), &(obj->resource), CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
1034 obj->alloc_clock = true;
1035
1036 if (result == CY_RSLT_SUCCESS)
1037 {
1038 result = _cyhal_utils_peri_pclk_assign_divider(_cyhal_scb_get_clock_index(obj->resource.block_num), &(obj->clock));
1039 }
1040
1041 if (result == CY_RSLT_SUCCESS)
1042 {
1043 result = _cyhal_spi_int_frequency(obj, _CYHAL_SPI_DEFAULT_SPEED, &obj->oversample_value);
1044 }
1045 }
1046 else
1047 {
1048 obj->clock = *cfg->clock;
1049 obj->alloc_clock = false;
1050
1051 /* Per CDT 315848 and 002-20730 Rev. *E:
1052 * For SPI, an integer clock divider must be used for both master and slave. */
1053 if ((_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(obj->clock.block) == (cy_en_divider_types_t)CYHAL_CLOCK_BLOCK_PERIPHERAL_16_5BIT) ||
1054 (_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(obj->clock.block) == (cy_en_divider_types_t)CYHAL_CLOCK_BLOCK_PERIPHERAL_24_5BIT))
1055 {
1056 result = CYHAL_SPI_RSLT_CLOCK_NOT_SUPPORTED;
1057 }
1058 }
1059 }
1060 #endif
1061
1062 if( result == CY_RSLT_SUCCESS)
1063 {
1064 result = _cyhal_spi_init_hw(obj, &cfg_local);
1065 }
1066
1067 /* Configuring rest of provided ssel signals */
1068 size_t ssel_idx = obj->active_ssel;
1069 while ((result == CY_RSLT_SUCCESS) && ((ssel_idx + 1) < sizeof(cfg->gpios.ssel) / sizeof(cfg->gpios.ssel[0])))
1070 {
1071 ++ssel_idx;
1072 if (NC != cfg->gpios.ssel[ssel_idx])
1073 {
1074 result = _cyhal_spi_ssel_config(obj, cfg->gpios.ssel[ssel_idx],
1075 (cyhal_spi_ssel_polarity_t)((cfg->config->ssPolarity >> ssel_idx) & 0x1),
1076 !obj->dc_configured);
1077 }
1078 }
1079 if(result != CY_RSLT_SUCCESS)
1080 {
1081 cyhal_spi_free(obj);
1082 }
1083 return result;
1084 }
1085
cyhal_spi_free(cyhal_spi_t * obj)1086 void cyhal_spi_free(cyhal_spi_t *obj)
1087 {
1088 if (NULL != obj->base)
1089 {
1090 _cyhal_scb_update_instance_data(obj->resource.block_num, NULL, NULL);
1091 Cy_SCB_SPI_Disable(obj->base, NULL);
1092 Cy_SCB_SPI_DeInit(obj->base);
1093 obj->base = NULL;
1094 }
1095
1096 if (obj->resource.type != CYHAL_RSC_INVALID)
1097 {
1098 uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
1099 _cyhal_system_irq_t irqn = _CYHAL_SCB_IRQ_N[scb_arr_index];
1100 _cyhal_irq_free(irqn);
1101
1102 if (false == obj->dc_configured)
1103 {
1104 cyhal_resource_inst_t rsc_to_free = { CYHAL_RSC_SCB, _cyhal_scb_get_block_index(obj->resource.block_num), obj->resource.channel_num };
1105 cyhal_hwmgr_free(&(rsc_to_free));
1106 }
1107 obj->resource.type = CYHAL_RSC_INVALID;
1108 }
1109
1110 if (false == obj->dc_configured)
1111 {
1112 _cyhal_utils_release_if_used(&(obj->pin_miso));
1113 _cyhal_utils_release_if_used(&(obj->pin_mosi));
1114 _cyhal_utils_release_if_used(&(obj->pin_sclk));
1115 for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
1116 {
1117 #if defined(COMPONENT_CAT5)
1118 if(obj->is_slave == false)
1119 {
1120 // In master mode, we have reserved this via cyhal_gpio_init which logs it as reserved
1121 // in cyhal_gpio.c static arrays. Need to free it similarly to mark it unreserved
1122
1123 // Potential side effects: gpio free also unregisters any callback and disconnects the pin.
1124 // If this becomes problematic, add new gpio free_btss function to unset those arrays
1125 cyhal_gpio_free((obj->pin_ssel[i]));
1126 }
1127 else
1128 #endif
1129 {
1130 _cyhal_utils_release_if_used(&(obj->pin_ssel[i]));
1131 }
1132 }
1133 }
1134
1135 if (obj->alloc_clock)
1136 {
1137 cyhal_clock_free(&(obj->clock));
1138 obj->alloc_clock = false;
1139 }
1140 }
1141
_cyhal_ssel_switch_state(cyhal_spi_t * obj,uint8_t ssel_idx,bool ssel_activate)1142 static void _cyhal_ssel_switch_state(cyhal_spi_t *obj, uint8_t ssel_idx, bool ssel_activate)
1143 {
1144 if ((!obj->is_slave) && (CYHAL_NC_PIN_VALUE != obj->pin_ssel[ssel_idx]))
1145 {
1146 /* Situations described:
1147 * ssel_activate = true (need to set SSEL into active state)
1148 * CY_SCB_SPI_ACTIVE_LOW - writing 0 to ssel pin
1149 * CY_SCB_SPI_ACTIVE_HIGH - writing 1 to ssel pin
1150 * ssel_activate = false (need to set SSEL into inactive state)
1151 * CY_SCB_SPI_ACTIVE_LOW - writing 1 to ssel pin
1152 * CY_SCB_SPI_ACTIVE_HIGH - writing 0 to ssel pin */
1153 bool ssel_state = (CY_SCB_SPI_ACTIVE_LOW == obj->ssel_pol[ssel_idx]) ? !ssel_activate : ssel_activate;
1154 cyhal_gpio_write(obj->pin_ssel[ssel_idx], ssel_state);
1155 }
1156 }
1157
cyhal_spi_set_frequency(cyhal_spi_t * obj,uint32_t hz)1158 cy_rslt_t cyhal_spi_set_frequency(cyhal_spi_t *obj, uint32_t hz)
1159 {
1160 cy_rslt_t result = CY_RSLT_SUCCESS;
1161 cy_rslt_t scb_init_result = CY_RSLT_SUCCESS;
1162 uint8_t ovr_sample_val;
1163
1164 if (NULL == obj)
1165 {
1166 return CYHAL_SPI_RSLT_BAD_ARGUMENT;
1167 }
1168
1169 /* HAL is allowed to change clock settings since it owns the clock */
1170 if (obj->alloc_clock)
1171 {
1172 Cy_SCB_SPI_Disable(obj->base, &obj->context);
1173 result = _cyhal_spi_int_frequency(obj, hz, &ovr_sample_val);
1174
1175 /* No need to reconfigure slave since oversample value, that was changed in _cyhal_spi_int_frequency, in slave is ignored */
1176 if ((CY_RSLT_SUCCESS == result) && !obj->is_slave && (obj->oversample_value != ovr_sample_val))
1177 {
1178 cy_stc_scb_spi_config_t config_structure = _cyhal_spi_default_config;
1179 Cy_SCB_SPI_DeInit(obj->base);
1180 config_structure.spiMode = obj->is_slave == false
1181 ? CY_SCB_SPI_MASTER
1182 : CY_SCB_SPI_SLAVE;
1183 config_structure.enableMsbFirst = obj->msb_first;
1184 config_structure.sclkMode = obj->clk_mode;
1185 config_structure.rxDataWidth = obj->data_bits;
1186 config_structure.txDataWidth = obj->data_bits;
1187 config_structure.oversample = ovr_sample_val;
1188 obj->oversample_value = ovr_sample_val;
1189 scb_init_result = (cy_rslt_t)Cy_SCB_SPI_Init(obj->base, &config_structure, &(obj->context));
1190 }
1191 if(CY_RSLT_SUCCESS == scb_init_result)
1192 {
1193 Cy_SCB_SPI_Enable(obj->base);
1194 }
1195 }
1196 else
1197 {
1198 result = CYHAL_SPI_RSLT_CLOCK_ERROR;
1199 }
1200
1201 return result;
1202 }
1203
cyhal_spi_select_active_ssel(cyhal_spi_t * obj,cyhal_gpio_t ssel)1204 cy_rslt_t cyhal_spi_select_active_ssel(cyhal_spi_t *obj, cyhal_gpio_t ssel)
1205 {
1206 CY_ASSERT(NULL != obj);
1207 CY_ASSERT(NULL != obj->base);
1208
1209 // If all obj->pin_ssel[] = NC then return Error code
1210 bool nc_flag = true;
1211 for (uint8_t ssel_idx = 0; ssel_idx < _CYHAL_SPI_SSEL_NUM; ssel_idx++)
1212 {
1213 if(obj->pin_ssel[ssel_idx] != NC)
1214 {
1215 nc_flag = false;
1216 break;
1217 }
1218 }
1219
1220 if(nc_flag)
1221 {
1222 return CYHAL_SPI_RSLT_ERR_CANNOT_SWITCH_SSEL;
1223 }
1224
1225 if ((NC != ssel) && (_CYHAL_SPI_PENDING_NONE == obj->pending))
1226 {
1227 for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
1228 {
1229 if(obj->pin_ssel[i] == ssel)
1230 {
1231 Cy_SCB_SPI_SetActiveSlaveSelect(obj->base, (cy_en_scb_spi_slave_select_t)i);
1232 obj->active_ssel = i;
1233 return CY_RSLT_SUCCESS;
1234 }
1235 }
1236 }
1237 return CYHAL_SPI_RSLT_ERR_CANNOT_SWITCH_SSEL;
1238 }
1239
cyhal_spi_slave_select_config(cyhal_spi_t * obj,cyhal_gpio_t ssel,cyhal_spi_ssel_polarity_t polarity)1240 cy_rslt_t cyhal_spi_slave_select_config(cyhal_spi_t *obj, cyhal_gpio_t ssel, cyhal_spi_ssel_polarity_t polarity)
1241 {
1242 CY_ASSERT(NULL != obj);
1243 return _cyhal_spi_ssel_config(obj, ssel, polarity, true);
1244 }
1245
cyhal_spi_recv(cyhal_spi_t * obj,uint32_t * value)1246 cy_rslt_t cyhal_spi_recv(cyhal_spi_t *obj, uint32_t *value)
1247 {
1248 if (NULL == obj)
1249 return CYHAL_SPI_RSLT_BAD_ARGUMENT;
1250
1251 if (_cyhal_scb_pm_transition_pending())
1252 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1253
1254 uint32_t read_value = CY_SCB_SPI_RX_NO_DATA;
1255 const uint32_t fill_in = 0xffffffffUL; /* PDL Fill in value */
1256 uint32_t count = 0;
1257
1258 if (!obj->is_slave)
1259 {
1260 _cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_ACTIVATE);
1261
1262 /* Clear FIFOs */
1263 Cy_SCB_SPI_ClearTxFifo(obj->base);
1264 Cy_SCB_SPI_ClearRxFifo(obj->base);
1265
1266 while (count == 0)
1267 {
1268 count = Cy_SCB_SPI_Write(obj->base, fill_in);
1269 }
1270
1271 while (Cy_SCB_SPI_IsTxComplete(obj->base) == false) { }
1272 while (Cy_SCB_SPI_GetNumInRxFifo(obj->base) == 0) { } /* Wait for RX FIFO not empty */
1273 _cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_DEACTIVATE);
1274 }
1275
1276 while (read_value == CY_SCB_SPI_RX_NO_DATA)
1277 {
1278 read_value = Cy_SCB_SPI_Read(obj->base);
1279 }
1280 *value = read_value;
1281 return CY_RSLT_SUCCESS;
1282 }
1283
cyhal_spi_send(cyhal_spi_t * obj,uint32_t value)1284 cy_rslt_t cyhal_spi_send(cyhal_spi_t *obj, uint32_t value)
1285 {
1286 if (NULL == obj)
1287 return CYHAL_SPI_RSLT_BAD_ARGUMENT;
1288
1289 if (_cyhal_scb_pm_transition_pending())
1290 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1291
1292 uint32_t count = 0;
1293 cy_rslt_t result = CY_RSLT_SUCCESS;
1294
1295 if (!obj->is_slave)
1296 {
1297 _cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_ACTIVATE);
1298
1299 /* Clear FIFOs */
1300 Cy_SCB_SPI_ClearTxFifo(obj->base);
1301 Cy_SCB_SPI_ClearRxFifo(obj->base);
1302 }
1303
1304 while (count == 0)
1305 {
1306 count = Cy_SCB_SPI_Write(obj->base, value);
1307 }
1308
1309 if (!obj->is_slave)
1310 {
1311 while (Cy_SCB_SPI_IsTxComplete(obj->base) == false) { }
1312 while (Cy_SCB_SPI_GetNumInRxFifo(obj->base) == 0) { } /* Wait for RX FIFO not empty */
1313 _cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_DEACTIVATE);
1314 (void)Cy_SCB_SPI_Read(obj->base);
1315 }
1316
1317 return result;
1318 }
1319
1320
cyhal_spi_slave_read(cyhal_spi_t * obj,uint8_t * dst_buff,uint16_t * size,uint32_t timeout)1321 cy_rslt_t cyhal_spi_slave_read(cyhal_spi_t *obj, uint8_t *dst_buff, uint16_t *size, uint32_t timeout)
1322 {
1323 cy_rslt_t status = CYHAL_SPI_RSLT_BAD_ARGUMENT;
1324
1325 if ((dst_buff != NULL) && (size != NULL))
1326 {
1327 /* Wait until the master start writing or any data will be in the slave RX buffer */
1328 status = _cyhal_spi_wait_for_op(obj, _CYHAL_SPI_WAIT_OP_RD_NOT_BUSY, &timeout);
1329
1330 if (CY_RSLT_SUCCESS == status)
1331 {
1332 /* Wait until the master finish writing */
1333 status = _cyhal_spi_wait_for_op(obj, _CYHAL_SPI_WAIT_OP_RD_BUSY, &timeout);
1334 }
1335
1336 if (CY_RSLT_SUCCESS == status)
1337 {
1338 *size = _CYHAL_SCB_BYTES_TO_COPY(cyhal_spi_readable(obj), *size);
1339 *size = Cy_SCB_SPI_ReadArray(obj->base, (void*)dst_buff, (uint32_t)*size);
1340 }
1341 }
1342 return status;
1343 }
1344
cyhal_spi_slave_write(cyhal_spi_t * obj,const uint8_t * src_buff,uint16_t * size,uint32_t timeout)1345 cy_rslt_t cyhal_spi_slave_write(cyhal_spi_t *obj, const uint8_t *src_buff, uint16_t *size, uint32_t timeout)
1346 {
1347 cy_rslt_t status = CYHAL_SPI_RSLT_BAD_ARGUMENT;
1348
1349 if ((src_buff != NULL) && (size != NULL))
1350 {
1351 status = cyhal_spi_transfer_async(obj, src_buff, (size_t)*size, NULL, 0U);
1352
1353 if (CY_RSLT_SUCCESS == status)
1354 {
1355 /* Wait until the slave finish writing */
1356 status = _cyhal_spi_wait_for_op(obj, _CYHAL_SPI_WAIT_OP_WR, &timeout);
1357 }
1358 }
1359 return status;
1360 }
1361
cyhal_spi_transfer(cyhal_spi_t * obj,const uint8_t * tx,size_t tx_length,uint8_t * rx,size_t rx_length,uint8_t write_fill)1362 cy_rslt_t cyhal_spi_transfer(cyhal_spi_t *obj, const uint8_t *tx, size_t tx_length, uint8_t *rx, size_t rx_length, uint8_t write_fill)
1363 {
1364 if (NULL == obj)
1365 return CYHAL_SPI_RSLT_BAD_ARGUMENT;
1366
1367 if (_cyhal_scb_pm_transition_pending())
1368 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1369
1370 obj->write_fill = write_fill;
1371 cy_rslt_t rslt = cyhal_spi_transfer_async(obj, tx, tx_length, rx, rx_length);
1372 if (rslt == CY_RSLT_SUCCESS)
1373 {
1374 while (obj->pending != _CYHAL_SPI_PENDING_NONE) { } /* Wait for async transfer to complete */
1375 }
1376 obj->write_fill = (uint8_t) CY_SCB_SPI_DEFAULT_TX;
1377 return rslt;
1378 }
1379
cyhal_spi_transfer_async(cyhal_spi_t * obj,const uint8_t * tx,size_t tx_length,uint8_t * rx,size_t rx_length)1380 cy_rslt_t cyhal_spi_transfer_async(cyhal_spi_t *obj, const uint8_t *tx, size_t tx_length, uint8_t *rx, size_t rx_length)
1381 {
1382 if (NULL == obj)
1383 return CYHAL_SPI_RSLT_BAD_ARGUMENT;
1384
1385 if (_cyhal_scb_pm_transition_pending())
1386 return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
1387
1388 cy_en_scb_spi_status_t spi_status;
1389
1390 _cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_ACTIVATE);
1391 obj->is_async = true;
1392
1393 uint8_t arr_size_modifier = 0;
1394 if (obj->data_bits <= 8)
1395 {
1396 arr_size_modifier = 1;
1397 }
1398 else if (obj->data_bits <= 16)
1399 {
1400 arr_size_modifier = 2;
1401 }
1402 else
1403 {
1404 arr_size_modifier = 4;
1405 }
1406
1407 if ((tx_length % arr_size_modifier != 0) || (rx_length % arr_size_modifier != 0))
1408 return CYHAL_SPI_RSLT_BAD_ARGUMENT;
1409
1410 size_t tx_words = tx_length / arr_size_modifier;
1411 size_t rx_words = rx_length / arr_size_modifier;
1412
1413
1414 /* Setup transfer */
1415 obj->rx_buffer = NULL;
1416 obj->tx_buffer = NULL;
1417 #if !defined(_CYHAL_SPI_ASYMM_PDL_FUNC_AVAIL)
1418
1419 if (tx_words > rx_words)
1420 {
1421 if (rx_words > 0)
1422 {
1423 /* I) write + read, II) write only */
1424 obj->pending = _CYHAL_SPI_PENDING_TX_RX;
1425
1426 obj->tx_buffer = tx + (rx_words * arr_size_modifier);
1427 obj->tx_buffer_size = tx_words - rx_words;
1428
1429 tx_words = rx_words; // Use tx_words to store entire transfer length
1430 }
1431 else
1432 {
1433 /* I) write only */
1434 obj->pending = _CYHAL_SPI_PENDING_TX;
1435
1436 rx = NULL;
1437 }
1438 }
1439 else if (rx_words > tx_words)
1440 {
1441 if (tx_words > 0)
1442 {
1443 /* I) write + read, II) read only */
1444 obj->pending = _CYHAL_SPI_PENDING_TX_RX;
1445
1446 obj->rx_buffer = rx + (tx_words * arr_size_modifier);
1447 obj->rx_buffer_size = rx_words - tx_words;
1448 }
1449 else
1450 {
1451 /* I) read only. */
1452 obj->pending = _CYHAL_SPI_PENDING_RX;
1453
1454 obj->rx_buffer = rx_words > 1 ? rx + 1 : NULL;
1455 obj->rx_buffer_size = rx_words - 1;
1456 tx = &obj->write_fill;
1457 tx_words = 1;
1458 }
1459 }
1460 else
1461 {
1462 /* RX and TX of the same size: I) write + read. */
1463 obj->pending = _CYHAL_SPI_PENDING_TX_RX;
1464 }
1465 spi_status = Cy_SCB_SPI_Transfer(obj->base, (void *)tx, rx, tx_words, &obj->context);
1466 #else // !defined(_CYHAL_SPI_ASYMM_PDL_FUNC_AVAIL)
1467
1468 CY_UNUSED_PARAMETER(arr_size_modifier);
1469 if (tx_words != rx_words)
1470 {
1471 if(tx_words == 0)
1472 {
1473 obj->pending = _CYHAL_SPI_PENDING_RX;
1474 tx = NULL;
1475 }
1476 else if (rx_words == 0)
1477 {
1478 obj->pending = _CYHAL_SPI_PENDING_TX;
1479 rx = NULL;
1480 }
1481 else
1482 {
1483 obj->pending = _CYHAL_SPI_PENDING_TX_RX;
1484 }
1485 spi_status = Cy_SCB_SPI_Transfer_Buffer(obj->base, (void *)tx, (void *)rx, tx_words, rx_words, obj->write_fill, &obj->context);
1486 }
1487 else
1488 {
1489 obj->pending = _CYHAL_SPI_PENDING_TX_RX;
1490 spi_status = Cy_SCB_SPI_Transfer(obj->base, (void *)tx, rx, tx_words, &obj->context);
1491 }
1492
1493 #endif // _CYHAL_SPI_ASYMM_PDL_FUNC_AVAIL
1494 return spi_status == CY_SCB_SPI_SUCCESS
1495 ? CY_RSLT_SUCCESS
1496 : CYHAL_SPI_RSLT_TRANSFER_ERROR;
1497 }
1498
cyhal_spi_is_busy(cyhal_spi_t * obj)1499 bool cyhal_spi_is_busy(cyhal_spi_t *obj)
1500 {
1501 return Cy_SCB_SPI_IsBusBusy(obj->base) || (_CYHAL_SPI_PENDING_NONE != obj->pending);
1502 }
1503
cyhal_spi_abort_async(cyhal_spi_t * obj)1504 cy_rslt_t cyhal_spi_abort_async(cyhal_spi_t *obj)
1505 {
1506 if (NULL == obj)
1507 {
1508 return CYHAL_SPI_RSLT_BAD_ARGUMENT;
1509 }
1510
1511 Cy_SCB_SPI_AbortTransfer(obj->base, &(obj->context));
1512 obj->pending = _CYHAL_SPI_PENDING_NONE;
1513 return CY_RSLT_SUCCESS;
1514 }
1515
cyhal_spi_register_callback(cyhal_spi_t * obj,cyhal_spi_event_callback_t callback,void * callback_arg)1516 void cyhal_spi_register_callback(cyhal_spi_t *obj, cyhal_spi_event_callback_t callback, void *callback_arg)
1517 {
1518 uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
1519 obj->callback_data.callback = (cy_israddress) callback;
1520 obj->callback_data.callback_arg = callback_arg;
1521 cyhal_system_critical_section_exit(savedIntrStatus);
1522 Cy_SCB_SPI_RegisterCallback(obj->base, _cyhal_spi_cb_wrapper, &(obj->context));
1523
1524 obj->irq_cause = 0;
1525 }
1526
cyhal_spi_enable_event(cyhal_spi_t * obj,cyhal_spi_event_t event,uint8_t intr_priority,bool enable)1527 void cyhal_spi_enable_event(cyhal_spi_t *obj, cyhal_spi_event_t event, uint8_t intr_priority, bool enable)
1528 {
1529 if (enable)
1530 {
1531 obj->irq_cause |= event;
1532 }
1533 else
1534 {
1535 obj->irq_cause &= ~event;
1536 }
1537
1538 uint8_t scb_arr_index = _cyhal_scb_get_block_index(obj->resource.block_num);
1539 _cyhal_system_irq_t irqn = _CYHAL_SCB_IRQ_N[scb_arr_index];
1540 _cyhal_irq_set_priority(irqn, intr_priority);
1541 }
1542
cyhal_spi_set_fifo_level(cyhal_spi_t * obj,cyhal_spi_fifo_type_t type,uint16_t level)1543 cy_rslt_t cyhal_spi_set_fifo_level(cyhal_spi_t *obj, cyhal_spi_fifo_type_t type, uint16_t level)
1544 {
1545 return _cyhal_scb_set_fifo_level(obj->base, (cyhal_scb_fifo_type_t)type, level);
1546 }
1547
cyhal_spi_enable_output(cyhal_spi_t * obj,cyhal_spi_output_t output,cyhal_source_t * source)1548 cy_rslt_t cyhal_spi_enable_output(cyhal_spi_t *obj, cyhal_spi_output_t output, cyhal_source_t *source)
1549 {
1550 return _cyhal_scb_enable_output(obj->resource, (cyhal_scb_output_t)output, source);
1551 }
1552
cyhal_spi_disable_output(cyhal_spi_t * obj,cyhal_spi_output_t output)1553 cy_rslt_t cyhal_spi_disable_output(cyhal_spi_t *obj, cyhal_spi_output_t output)
1554 {
1555 CY_UNUSED_PARAMETER(obj);
1556 return _cyhal_scb_disable_output((cyhal_scb_output_t)output);
1557 }
1558
cyhal_spi_readable(cyhal_spi_t * obj)1559 uint32_t cyhal_spi_readable(cyhal_spi_t *obj)
1560 {
1561 return Cy_SCB_SPI_GetNumInRxFifo(obj->base);
1562 }
1563
cyhal_spi_writable(cyhal_spi_t * obj)1564 uint32_t cyhal_spi_writable(cyhal_spi_t *obj)
1565 {
1566 return Cy_SCB_GetFifoSize(obj->base) - Cy_SCB_SPI_GetNumInTxFifo(obj->base);
1567 }
1568
cyhal_spi_clear(cyhal_spi_t * obj)1569 cy_rslt_t cyhal_spi_clear(cyhal_spi_t *obj)
1570 {
1571 Cy_SCB_SPI_ClearRxFifo(obj->base);
1572 Cy_SCB_SPI_ClearTxFifo(obj->base);
1573
1574 return CY_RSLT_SUCCESS;
1575 }
1576
1577 #if defined(__cplusplus)
1578 }
1579 #endif
1580
1581 #endif /* CYHAL_DRIVER_AVAILABLE_SPI */
1582