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