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