1 /*
2  * Copyright (c) 2013 - 2025, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <nrfx.h>
35 
36 #if NRFX_CHECK(NRFX_SPIS_ENABLED)
37 
38 #if !NRFX_FEATURE_PRESENT(NRFX_SPIS, _ENABLED)
39 #error "No enabled SPIS instances. Check <nrfx_config.h>."
40 #endif
41 
42 #include <nrfx_spis.h>
43 #include "prs/nrfx_prs.h"
44 
45 #define NRFX_LOG_MODULE SPIS
46 #include <nrfx_log.h>
47 
48 #define EVT_TO_STR(event)                                           \
49     (event == NRF_SPIS_EVENT_ACQUIRED ? "NRF_SPIS_EVENT_ACQUIRED" : \
50     (event == NRF_SPIS_EVENT_END      ? "NRF_SPIS_EVENT_END"      : \
51                                         "UNKNOWN ERROR"))
52 
53 #define SPISX_LENGTH_VALIDATE(periph_name, prefix, i, drv_inst_idx, rx_len, tx_len) \
54     (((drv_inst_idx) == NRFX_CONCAT(NRFX_, periph_name, prefix, i, _INST_IDX)) && \
55      NRFX_EASYDMA_LENGTH_VALIDATE(NRFX_CONCAT(periph_name, prefix, i), rx_len, tx_len))
56 
57 #define SPIS_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len)    \
58         (NRFX_FOREACH_ENABLED(SPIS, SPISX_LENGTH_VALIDATE, (||), (0), drv_inst_idx, rx_len, tx_len))
59 
60 #if NRFX_CHECK(NRFX_SPIS_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
61 #include <nrfx_gpiote.h>
62 #define USE_DMA_ISSUE_WORKAROUND
63 // This handler is called by the GPIOTE driver when a falling edge is detected
64 // on the CSN line. There is no need to do anything here. The handling of the
65 // interrupt itself provides a protection for DMA transfers.
csn_event_handler(nrfx_gpiote_pin_t pin,nrfx_gpiote_trigger_t trigger,void * p_context)66 static void csn_event_handler(nrfx_gpiote_pin_t     pin,
67                               nrfx_gpiote_trigger_t trigger,
68                               void *                p_context)
69 {
70     (void)pin;
71     (void)trigger;
72     (void)p_context;
73 }
74 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
75 static nrfx_gpiote_t const gpiote = NRFX_GPIOTE_INSTANCE(0);
76 #endif
77 #endif
78 
79 
80 /** @brief States of the SPI transaction state machine. */
81 typedef enum
82 {
83     SPIS_STATE_INIT,                                 /**< Initialization state. In this state the module waits for a call to @ref spi_slave_buffers_set. */
84     SPIS_BUFFER_RESOURCE_REQUESTED,                  /**< State where the configuration of the memory buffers, which are to be used in SPI transaction, has started. */
85     SPIS_BUFFER_RESOURCE_CONFIGURED,                 /**< State where the configuration of the memory buffers, which are to be used in SPI transaction, has completed. */
86     SPIS_XFER_COMPLETED                              /**< State where SPI transaction has been completed. */
87 } nrfx_spis_state_t;
88 
89 /** @brief SPIS control block - driver instance local data. */
90 typedef struct
91 {
92     volatile uint32_t          tx_buffer_size;  //!< SPI slave TX buffer size in bytes.
93     volatile uint32_t          rx_buffer_size;  //!< SPI slave RX buffer size in bytes.
94     nrfx_spis_event_handler_t  handler;         //!< SPI event handler.
95     volatile const uint8_t *   tx_buffer;       //!< SPI slave TX buffer.
96     volatile uint8_t *         rx_buffer;       //!< SPI slave RX buffer.
97     nrfx_drv_state_t           state;           //!< driver initialization state.
98     volatile nrfx_spis_state_t spi_state;       //!< SPI slave state.
99     void *                     p_context;       //!< Context set on initialization.
100     bool                       skip_gpio_cfg;
101 #if defined(USE_DMA_ISSUE_WORKAROUND)
102     uint32_t                   csn_pin;
103     uint8_t                    gpiote_ch;
104 #endif
105 } spis_cb_t;
106 
107 static spis_cb_t m_cb[NRFX_SPIS_ENABLED_COUNT];
108 
pins_configure(nrfx_spis_config_t const * p_config)109 static void pins_configure(nrfx_spis_config_t const * p_config)
110 {
111     nrf_gpio_cfg(p_config->sck_pin,
112                  NRF_GPIO_PIN_DIR_INPUT,
113                  NRF_GPIO_PIN_INPUT_CONNECT,
114                  NRF_GPIO_PIN_NOPULL,
115                  NRF_GPIO_PIN_S0S1,
116                  NRF_GPIO_PIN_NOSENSE);
117 #if NRF_GPIO_HAS_CLOCKPIN && defined(NRF_SPIS_CLOCKPIN_SCK_NEEDED)
118     nrfy_gpio_pin_clock_set(p_config->sck_pin, true);
119 #endif
120 
121     if (p_config->mosi_pin != NRF_SPIS_PIN_NOT_CONNECTED)
122     {
123         nrf_gpio_cfg(p_config->mosi_pin,
124                      NRF_GPIO_PIN_DIR_INPUT,
125                      NRF_GPIO_PIN_INPUT_CONNECT,
126                      NRF_GPIO_PIN_NOPULL,
127                      NRF_GPIO_PIN_S0S1,
128                      NRF_GPIO_PIN_NOSENSE);
129     }
130 
131     if (p_config->miso_pin != NRF_SPIS_PIN_NOT_CONNECTED)
132     {
133         nrf_gpio_cfg(p_config->miso_pin,
134                      NRF_GPIO_PIN_DIR_INPUT,
135                      NRF_GPIO_PIN_INPUT_CONNECT,
136                      NRF_GPIO_PIN_NOPULL,
137                      p_config->miso_drive,
138                      NRF_GPIO_PIN_NOSENSE);
139 #if NRF_GPIO_HAS_CLOCKPIN && defined(NRF_SPIS_CLOCKPIN_MISO_NEEDED)
140         nrfy_gpio_pin_clock_set(p_config->miso_pin, true);
141 #endif
142     }
143 
144     nrf_gpio_cfg(p_config->csn_pin,
145                  NRF_GPIO_PIN_DIR_INPUT,
146                  NRF_GPIO_PIN_INPUT_CONNECT,
147                  p_config->csn_pullup,
148                  NRF_GPIO_PIN_S0S1,
149                  NRF_GPIO_PIN_NOSENSE);
150 }
151 
spis_configure(nrfx_spis_t const * p_instance,nrfx_spis_config_t const * p_config)152 static bool spis_configure(nrfx_spis_t const *        p_instance,
153                            nrfx_spis_config_t const * p_config)
154 {
155     NRF_SPIS_Type * p_spis = p_instance->p_reg;
156     if (p_config->mode > NRF_SPIS_MODE_3)
157     {
158         return false;
159     }
160 
161     if (!p_config->skip_gpio_cfg)
162     {
163         pins_configure(p_config);
164     }
165 
166     if (!p_config->skip_psel_cfg)
167     {
168         nrf_spis_pins_set(p_spis,
169                           p_config->sck_pin,
170                           p_config->mosi_pin,
171                           p_config->miso_pin,
172                           p_config->csn_pin);
173     }
174 
175 #if defined(USE_DMA_ISSUE_WORKAROUND)
176     spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
177 
178     // If the GPIOTE channel was already used with a CSN pin, deinitialize it
179     // first as that pin number may be different now.
180     if (p_cb->csn_pin != NRF_SPIS_PIN_NOT_CONNECTED)
181     {
182 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
183         nrfx_gpiote_pin_uninit(&gpiote, p_cb->csn_pin);
184 #else
185         nrfx_gpiote_pin_uninit(p_cb->csn_pin);
186 #endif
187         p_cb->csn_pin = NRF_SPIS_PIN_NOT_CONNECTED;
188     }
189 
190     // Get the CSN pin number from the PSEL register in the peripheral
191     // as in p_config that pin number may be omitted.
192     uint32_t csn_pin = nrf_spis_csn_pin_get(p_spis);
193 
194     // Configure a GPIOTE channel to generate interrupts on each falling edge
195     // on the CSN line. Handling of these interrupts will make the CPU active
196     // and thus will protect the DMA transfers started by SPIS right after it
197     // is selected for communication.
198     nrfx_gpiote_trigger_config_t trig_config = {
199         .trigger = NRFX_GPIOTE_TRIGGER_HITOLO,
200         .p_in_channel = &p_cb->gpiote_ch
201     };
202     nrfx_gpiote_handler_config_t hndl_config = {
203         .handler = csn_event_handler
204     };
205 
206 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
207     nrfx_gpiote_input_pin_config_t config = {
208         .p_pull_config    = NULL,
209         .p_trigger_config = &trig_config,
210         .p_handler_config = &hndl_config
211     };
212     nrfx_err_t err_code = nrfx_gpiote_input_configure(&gpiote, csn_pin, &config);
213 #else
214     nrfx_err_t err_code = nrfx_gpiote_input_configure(csn_pin, NULL, &trig_config, &hndl_config);
215 #endif
216     if (err_code != NRFX_SUCCESS)
217     {
218         NRFX_LOG_ERROR("Function: %s, error code: %s.",
219                        __func__,
220                        NRFX_LOG_ERROR_STRING_GET(err_code));
221         return false;
222     }
223 
224 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
225     nrfx_gpiote_trigger_enable(&gpiote, csn_pin, true);
226 #else
227     nrfx_gpiote_trigger_enable(csn_pin, true);
228 #endif
229 
230     p_cb->csn_pin = csn_pin;
231 #endif
232 
233     // Configure SPI mode.
234     nrf_spis_configure(p_spis, p_config->mode, p_config->bit_order);
235 
236     // Configure DEF and ORC characters.
237     nrf_spis_def_set(p_spis, p_config->def);
238     nrf_spis_orc_set(p_spis, p_config->orc);
239 
240     // Clear possible pending events.
241     nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_END);
242     nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_ACQUIRED);
243 
244     // Enable END_ACQUIRE shortcut.
245     nrf_spis_shorts_enable(p_spis, NRF_SPIS_SHORT_END_ACQUIRE);
246 
247     NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg), p_config->irq_priority);
248     NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg));
249 
250     return true;
251 }
252 
nrfx_spis_init(nrfx_spis_t const * p_instance,nrfx_spis_config_t const * p_config,nrfx_spis_event_handler_t event_handler,void * p_context)253 nrfx_err_t nrfx_spis_init(nrfx_spis_t const *        p_instance,
254                           nrfx_spis_config_t const * p_config,
255                           nrfx_spis_event_handler_t  event_handler,
256                           void *                     p_context)
257 {
258     NRFX_ASSERT(p_config);
259     NRFX_ASSERT(event_handler);
260 
261     spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
262     nrfx_err_t err_code;
263 
264     NRF_SPIS_Type * p_spis = p_instance->p_reg;
265 
266     if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
267     {
268 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
269         err_code = NRFX_ERROR_ALREADY;
270 #else
271         err_code = NRFX_ERROR_INVALID_STATE;
272 #endif
273         NRFX_LOG_WARNING("Function: %s, error code: %s.",
274                          __func__,
275                          NRFX_LOG_ERROR_STRING_GET(err_code));
276         return err_code;
277     }
278 
279 #if NRFX_CHECK(NRFX_PRS_ENABLED)
280     static nrfx_irq_handler_t const irq_handlers[NRFX_SPIS_ENABLED_COUNT] = {
281         NRFX_INSTANCE_IRQ_HANDLERS_LIST(SPIS, spis)
282     };
283     if (nrfx_prs_acquire(p_spis,
284             irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS)
285     {
286         err_code = NRFX_ERROR_BUSY;
287         NRFX_LOG_WARNING("Function: %s, error code: %s.",
288                          __func__,
289                          NRFX_LOG_ERROR_STRING_GET(err_code));
290         return err_code;
291     }
292 #endif // NRFX_CHECK(NRFX_PRS_ENABLED)
293 
294     p_cb->handler   = event_handler;
295     p_cb->p_context = p_context;
296 
297 #if defined(USE_DMA_ISSUE_WORKAROUND)
298     p_cb->csn_pin = NRF_SPIS_PIN_NOT_CONNECTED;
299 
300     // Allocate a GPIOTE channel that will be used to handle the anomaly 109
301     // (the GPIOTE driver may be already initialized at this point, by this
302     // driver when another SPIS instance is used or by an application code,
303     // so just ignore the returned value here).
304 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
305     (void)nrfx_gpiote_init(&gpiote, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
306     err_code = nrfx_gpiote_channel_alloc(&gpiote, &p_cb->gpiote_ch);
307 #else
308     (void)nrfx_gpiote_init(NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
309     err_code = nrfx_gpiote_channel_alloc(&p_cb->gpiote_ch);
310 #endif
311 
312     if (err_code != NRFX_SUCCESS)
313     {
314 #if NRFX_CHECK(NRFX_PRS_ENABLED)
315         nrfx_prs_release(p_spis);
316 #endif
317         err_code = NRFX_ERROR_INTERNAL;
318         NRFX_LOG_ERROR("Function: %s, error code: %s.",
319                         __func__,
320                         NRFX_LOG_ERROR_STRING_GET(err_code));
321         return err_code;
322     }
323 #endif
324 
325     if (p_config)
326     {
327         p_cb->skip_gpio_cfg = p_config->skip_gpio_cfg;
328 
329         if (!spis_configure(p_instance, p_config))
330         {
331 #if defined(USE_DMA_ISSUE_WORKAROUND)
332 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
333             nrfx_gpiote_channel_free(&gpiote, p_cb->gpiote_ch);
334 #else
335             nrfx_gpiote_channel_free(p_cb->gpiote_ch);
336 #endif
337 #endif
338             err_code = NRFX_ERROR_INVALID_PARAM;
339             NRFX_LOG_WARNING("Function: %s, error code: %s.",
340                             __func__,
341                             NRFX_LOG_ERROR_STRING_GET(err_code));
342 #if NRFX_CHECK(NRFX_PRS_ENABLED)
343             nrfx_prs_release(p_spis);
344 #endif
345             return err_code;
346         }
347     }
348 
349     nrf_spis_rx_buffer_set(p_spis, NULL, 0);
350     nrf_spis_tx_buffer_set(p_spis, NULL, 0);
351 
352     p_cb->spi_state = SPIS_STATE_INIT;
353     // Enable IRQ.
354     nrf_spis_int_enable(p_spis, NRF_SPIS_INT_ACQUIRED_MASK |
355                                 NRF_SPIS_INT_END_MASK);
356 
357     p_cb->state = NRFX_DRV_STATE_INITIALIZED;
358 
359     // Enable SPI slave device.
360     nrf_spis_enable(p_spis);
361 
362     NRFX_LOG_INFO("Initialized.");
363     return NRFX_SUCCESS;
364 }
365 
nrfx_spis_reconfigure(nrfx_spis_t const * p_instance,nrfx_spis_config_t const * p_config)366 nrfx_err_t nrfx_spis_reconfigure(nrfx_spis_t const *        p_instance,
367                                  nrfx_spis_config_t const * p_config)
368 {
369     NRFX_ASSERT(p_config);
370 
371     nrfx_err_t err_code;
372     spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
373 
374     if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
375     {
376         return NRFX_ERROR_INVALID_STATE;
377     }
378     nrf_spis_disable(p_instance->p_reg);
379     if (spis_configure(p_instance, p_config))
380     {
381         err_code = NRFX_SUCCESS;
382     }
383     else
384     {
385         err_code = NRFX_ERROR_INVALID_PARAM;
386     }
387     nrf_spis_enable(p_instance->p_reg);
388     return err_code;
389 }
390 
nrfx_spis_uninit(nrfx_spis_t const * p_instance)391 void nrfx_spis_uninit(nrfx_spis_t const * p_instance)
392 {
393     spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
394 
395     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
396 
397     NRF_SPIS_Type * p_spis = p_instance->p_reg;
398 
399 #if defined(USE_DMA_ISSUE_WORKAROUND)
400     if (p_cb->csn_pin != NRF_SPIS_PIN_NOT_CONNECTED)
401     {
402 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
403         nrfx_gpiote_pin_uninit(&gpiote, p_cb->csn_pin);
404 #else
405         nrfx_gpiote_pin_uninit(p_cb->csn_pin);
406 #endif
407     }
408 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
409     nrfx_gpiote_channel_free(&gpiote, p_cb->gpiote_ch);
410 #else
411     nrfx_gpiote_channel_free(p_cb->gpiote_ch);
412 #endif
413 #endif
414 
415     #define DISABLE_ALL 0xFFFFFFFF
416     nrf_spis_disable(p_spis);
417     NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));
418     nrf_spis_int_disable(p_spis, DISABLE_ALL);
419     #undef  DISABLE_ALL
420 
421     if (!p_cb->skip_gpio_cfg)
422     {
423         nrf_gpio_cfg_default(nrf_spis_sck_pin_get(p_spis));
424         nrf_gpio_cfg_default(nrf_spis_csn_pin_get(p_spis));
425 
426         uint32_t miso_pin = nrf_spis_miso_pin_get(p_spis);
427         if (miso_pin != NRF_SPIS_PIN_NOT_CONNECTED)
428         {
429             nrf_gpio_cfg_default(miso_pin);
430         }
431 
432         uint32_t mosi_pin = nrf_spis_mosi_pin_get(p_spis);
433         if (mosi_pin != NRF_SPIS_PIN_NOT_CONNECTED)
434         {
435             nrf_gpio_cfg_default(mosi_pin);
436         }
437     }
438 
439 #if NRFX_CHECK(NRFX_PRS_ENABLED)
440     nrfx_prs_release(p_spis);
441 #endif
442 
443     p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
444     NRFX_LOG_INFO("Uninitialized.");
445 }
446 
nrfx_spis_init_check(nrfx_spis_t const * p_instance)447 bool nrfx_spis_init_check(nrfx_spis_t const * p_instance)
448 {
449     spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
450 
451     return (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
452 }
453 
454 /** @brief Function for executing the state entry action. */
spis_state_entry_action_execute(NRF_SPIS_Type * p_spis,spis_cb_t * p_cb)455 static void spis_state_entry_action_execute(NRF_SPIS_Type * p_spis,
456                                             spis_cb_t     * p_cb)
457 {
458     nrfx_spis_evt_t event;
459 
460     switch (p_cb->spi_state)
461     {
462         case SPIS_BUFFER_RESOURCE_REQUESTED:
463             nrf_spis_task_trigger(p_spis, NRF_SPIS_TASK_ACQUIRE);
464             break;
465 
466         case SPIS_BUFFER_RESOURCE_CONFIGURED:
467             event.evt_type  = NRFX_SPIS_BUFFERS_SET_DONE;
468             event.rx_amount = 0;
469             event.tx_amount = 0;
470 
471             NRFX_ASSERT(p_cb->handler != NULL);
472             p_cb->handler(&event, p_cb->p_context);
473             break;
474 
475         case SPIS_XFER_COMPLETED:
476             event.evt_type    = NRFX_SPIS_XFER_DONE;
477             event.rx_amount   = nrf_spis_rx_amount_get(p_spis);
478             event.tx_amount   = nrf_spis_tx_amount_get(p_spis);
479 
480 #if defined(__GNUC__)
481 #pragma GCC diagnostic push
482 #pragma GCC diagnostic ignored "-Wcast-qual"
483 #endif
484             event.p_tx_buf    = (void *)p_cb->tx_buffer;
485             event.p_rx_buf    = (void *)p_cb->rx_buffer;
486 #if defined(__GNUC__)
487 #pragma GCC diagnostic pop
488 #endif
489 
490             event.tx_buf_size = p_cb->tx_buffer_size;
491             event.rx_buf_size = p_cb->rx_buffer_size;
492 
493             NRFX_LOG_INFO("Transfer rx_len:%d.", event.rx_amount);
494             NRFX_LOG_DEBUG("Rx data:");
495             NRFX_LOG_HEXDUMP_DEBUG((uint8_t const *)p_cb->rx_buffer,
496                                    event.rx_amount * sizeof(p_cb->rx_buffer[0]));
497             NRFX_ASSERT(p_cb->handler != NULL);
498             p_cb->handler(&event, p_cb->p_context);
499             break;
500 
501         default:
502             // No implementation required.
503             break;
504     }
505 }
506 
507 /** @brief Function for changing the state of the SPI state machine.
508  *
509  * @param[in] p_spis    SPIS instance register.
510  * @param[in] p_cb      SPIS instance control block.
511  * @param[in] new_state State where the state machine transits to.
512  */
spis_state_change(NRF_SPIS_Type * p_spis,spis_cb_t * p_cb,nrfx_spis_state_t new_state)513 static void spis_state_change(NRF_SPIS_Type   * p_spis,
514                               spis_cb_t       * p_cb,
515                               nrfx_spis_state_t new_state)
516 {
517     p_cb->spi_state = new_state;
518     spis_state_entry_action_execute(p_spis, p_cb);
519 }
520 
nrfx_spis_buffers_set(nrfx_spis_t const * p_instance,uint8_t const * p_tx_buffer,size_t tx_buffer_length,uint8_t * p_rx_buffer,size_t rx_buffer_length)521 nrfx_err_t nrfx_spis_buffers_set(nrfx_spis_t const * p_instance,
522                                  uint8_t const *     p_tx_buffer,
523                                  size_t              tx_buffer_length,
524                                  uint8_t *           p_rx_buffer,
525                                  size_t              rx_buffer_length)
526 {
527     spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
528     nrfx_err_t err_code;
529 
530     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
531     NRFX_ASSERT(p_tx_buffer != NULL || tx_buffer_length == 0);
532     NRFX_ASSERT(p_rx_buffer != NULL || rx_buffer_length == 0);
533 
534     if (!SPIS_LENGTH_VALIDATE(p_instance->drv_inst_idx,
535                               rx_buffer_length,
536                               tx_buffer_length))
537     {
538         return NRFX_ERROR_INVALID_LENGTH;
539     }
540 
541     // EasyDMA requires that transfer buffers are placed in Data RAM region;
542     // signal error if they are not.
543     if ((p_tx_buffer != NULL && !nrfx_is_in_ram(p_tx_buffer)) ||
544         (p_rx_buffer != NULL && !nrfx_is_in_ram(p_rx_buffer)))
545     {
546         err_code = NRFX_ERROR_INVALID_ADDR;
547         NRFX_LOG_WARNING("Function: %s, error code: %s.",
548                          __func__,
549                          NRFX_LOG_ERROR_STRING_GET(err_code));
550         return err_code;
551     }
552 
553     switch (p_cb->spi_state)
554     {
555         case SPIS_STATE_INIT:
556         case SPIS_XFER_COMPLETED:
557         case SPIS_BUFFER_RESOURCE_CONFIGURED:
558             p_cb->tx_buffer      = p_tx_buffer;
559             p_cb->rx_buffer      = p_rx_buffer;
560             p_cb->tx_buffer_size = tx_buffer_length;
561             p_cb->rx_buffer_size = rx_buffer_length;
562             err_code             = NRFX_SUCCESS;
563 
564             spis_state_change(p_instance->p_reg, p_cb, SPIS_BUFFER_RESOURCE_REQUESTED);
565             break;
566 
567         case SPIS_BUFFER_RESOURCE_REQUESTED:
568             err_code = NRFX_ERROR_INVALID_STATE;
569             break;
570 
571         default:
572             // @note: execution of this code path would imply internal error in the design.
573             err_code = NRFX_ERROR_INTERNAL;
574             break;
575     }
576 
577     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
578     return err_code;
579 }
580 
irq_handler(NRF_SPIS_Type * p_spis,spis_cb_t * p_cb)581 static void irq_handler(NRF_SPIS_Type * p_spis, spis_cb_t * p_cb)
582 {
583     // @note: as multiple events can be pending for processing, the correct event processing order
584     // is as follows:
585     // - SPI semaphore acquired event.
586     // - SPI transaction complete event.
587 
588     // Check for SPI semaphore acquired event.
589     if (nrf_spis_event_check(p_spis, NRF_SPIS_EVENT_ACQUIRED))
590     {
591         nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_ACQUIRED);
592         NRFX_LOG_DEBUG("SPIS: Event: %s.", EVT_TO_STR(NRF_SPIS_EVENT_ACQUIRED));
593 
594         switch (p_cb->spi_state)
595         {
596             case SPIS_BUFFER_RESOURCE_REQUESTED:
597 
598 #if defined(__GNUC__)
599 #pragma GCC diagnostic push
600 #pragma GCC diagnostic ignored "-Wcast-qual"
601 #endif
602 
603                 nrf_spis_tx_buffer_set(p_spis, (uint8_t *)p_cb->tx_buffer, p_cb->tx_buffer_size);
604                 nrf_spis_rx_buffer_set(p_spis, (uint8_t *)p_cb->rx_buffer, p_cb->rx_buffer_size);
605 
606 #if defined(__GNUC__)
607 #pragma GCC diagnostic pop
608 #endif
609 
610                 nrf_spis_task_trigger(p_spis, NRF_SPIS_TASK_RELEASE);
611 
612                 spis_state_change(p_spis, p_cb, SPIS_BUFFER_RESOURCE_CONFIGURED);
613                 break;
614 
615             default:
616                 // No implementation required.
617                 break;
618         }
619     }
620 
621     // Check for SPI transaction complete event.
622     if (nrf_spis_event_check(p_spis, NRF_SPIS_EVENT_END))
623     {
624         nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_END);
625         NRFX_LOG_DEBUG("SPIS: Event: %s.", EVT_TO_STR(NRF_SPIS_EVENT_END));
626 
627         switch (p_cb->spi_state)
628         {
629             case SPIS_BUFFER_RESOURCE_CONFIGURED:
630                 spis_state_change(p_spis, p_cb, SPIS_XFER_COMPLETED);
631                 break;
632 
633             default:
634                 // No implementation required.
635                 break;
636         }
637     }
638 }
639 
640 NRFX_INSTANCE_IRQ_HANDLERS(SPIS, spis)
641 
642 #endif // NRFX_CHECK(NRFX_SPIS_ENABLED)
643