1 /*
2  * Copyright (c) 2015 - 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_PWM_ENABLED)
37 
38 #if !NRFX_FEATURE_PRESENT(NRFX_PWM, _ENABLED)
39 #error "No enabled PWM instances. Check <nrfx_config.h>."
40 #endif
41 
42 #include <nrfx_pwm.h>
43 #include <haly/nrfy_gpio.h>
44 
45 #define NRFX_LOG_MODULE PWM
46 #include <nrfx_log.h>
47 
48 #if NRFX_CHECK(NRFX_PWM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
49 // The workaround uses interrupts to wake up the CPU and ensure it is active
50 // when PWM is about to start a DMA transfer. For initial transfer, done when
51 // a playback is started via PPI, a specific EGU instance is used to generate
52 // an interrupt. During the playback, the PWM interrupt triggered on SEQEND
53 // event of a preceding sequence is used to protect the transfer done for
54 // the next sequence to be played.
55 #include <hal/nrf_egu.h>
56 #define USE_DMA_ISSUE_WORKAROUND
57 #endif
58 #if defined(USE_DMA_ISSUE_WORKAROUND)
59 #define EGU_IRQn(i)         EGU_IRQn_(i)
60 #define EGU_IRQn_(i)        SWI##i##_EGU##i##_IRQn
61 #define EGU_IRQHandler(i)   EGU_IRQHandler_(i)
62 #define EGU_IRQHandler_(i)  nrfx_egu_##i##_irq_handler
63 #define DMA_ISSUE_EGU_IDX           NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE
64 #define DMA_ISSUE_EGU               NRFX_CONCAT_2(NRF_EGU, DMA_ISSUE_EGU_IDX)
65 #define DMA_ISSUE_EGU_IRQn          EGU_IRQn(DMA_ISSUE_EGU_IDX)
66 #define DMA_ISSUE_EGU_IRQHandler    EGU_IRQHandler(DMA_ISSUE_EGU_IDX)
67 #endif
68 
69 // Control block - driver instance local data.
70 typedef struct
71 {
72 #if defined(USE_DMA_ISSUE_WORKAROUND)
73     uint32_t                  starting_task_address;
74 #endif
75     nrfx_pwm_handler_t        handler;
76     void *                    p_context;
77     nrfx_drv_state_t volatile state;
78     uint32_t                  flags;
79     bool                      skip_gpio_cfg;
80 } pwm_control_block_t;
81 static pwm_control_block_t m_cb[NRFX_PWM_ENABLED_COUNT];
82 
pins_configure(nrfx_pwm_config_t const * p_config)83 static void pins_configure(nrfx_pwm_config_t const * p_config)
84 {
85     // Nothing to do here if both GPIO configuration and pin selection are
86     // to be skipped (the pin numbers may be then even not specified).
87     if (p_config->skip_gpio_cfg && p_config->skip_psel_cfg)
88     {
89         return;
90     }
91 
92     for (uint8_t i = 0; i < NRF_PWM_CHANNEL_COUNT; ++i)
93     {
94         uint32_t output_pin = p_config->output_pins[i];
95         if (output_pin != NRF_PWM_PIN_NOT_CONNECTED)
96         {
97             if (p_config->pin_inverted[i])
98             {
99                 nrfy_gpio_pin_set(output_pin);
100             }
101             else
102             {
103                 nrfy_gpio_pin_clear(output_pin);
104             }
105 
106             nrfy_gpio_cfg_output(output_pin);
107         }
108     }
109 }
110 
pins_deconfigure(nrfx_pwm_t const * p_instance)111 static void pins_deconfigure(nrfx_pwm_t const * p_instance)
112 {
113     for (uint8_t ch_idx = 0; ch_idx < NRF_PWM_CHANNEL_COUNT; ch_idx++)
114     {
115         uint32_t pin = nrfy_pwm_pin_get(p_instance->p_reg, ch_idx);
116         if (pin != NRF_PWM_PIN_NOT_CONNECTED)
117         {
118             nrfy_gpio_cfg_default(pin);
119         }
120     }
121 }
122 
123 #if defined(USE_DMA_ISSUE_WORKAROUND)
apply_errata_109(nrfx_pwm_config_t const * p_config)124 static void apply_errata_109(nrfx_pwm_config_t const * p_config)
125 {
126     /* The workaround for nRF52 Anomaly 109 "protects" DMA transfers by handling
127      * interrupts generated on SEQEND0 and SEQEND1 events (this ensures that
128      * the 64 MHz clock is ready when data for the next sequence to be played
129      * is read). Therefore, the PWM interrupt must be enabled even if the event
130      * handler is not used.
131      */
132     NRFY_IRQ_PRIORITY_SET(DMA_ISSUE_EGU_IRQn, p_config->irq_priority);
133     NRFY_IRQ_ENABLE(DMA_ISSUE_EGU_IRQn);
134 }
135 #endif
136 
137 #define PWM_PIN_INIT(i, field) field[i]
138 
pwm_configure(nrfx_pwm_t const * p_instance,nrfx_pwm_config_t const * p_config)139 static void pwm_configure(nrfx_pwm_t const * p_instance, nrfx_pwm_config_t const * p_config)
140 {
141     if (!p_config->skip_gpio_cfg)
142     {
143         pins_configure(p_config);
144     }
145 
146     nrfy_pwm_config_t nrfy_config =
147     {
148         .output_pins   =
149         {
150             NRFX_LISTIFY(NRF_PWM_CHANNEL_COUNT, PWM_PIN_INIT, (,), p_config->output_pins)
151         },
152         .top_value     = p_config->top_value,
153         .base_clock    = p_config->base_clock,
154         .count_mode    = p_config->count_mode,
155         .load_mode     = p_config->load_mode,
156         .step_mode     = p_config->step_mode,
157         .skip_psel_cfg = p_config->skip_psel_cfg
158     };
159 
160     nrfy_pwm_periph_configure(p_instance->p_reg, &nrfy_config);
161     uint32_t to_clear = NRF_PWM_EVENT_LOOPSDONE | NRF_PWM_EVENT_SEQEND0 |
162                         NRF_PWM_EVENT_SEQEND1 | NRF_PWM_EVENT_STOPPED;
163     nrfy_pwm_int_init(p_instance->p_reg, to_clear, p_config->irq_priority, false);
164 
165 #if NRF_PWM_HAS_IDLEOUT
166     for (uint8_t channel = 0; channel < NRF_PWM_CHANNEL_COUNT; ++channel)
167     {
168         nrfy_pwm_channel_idle_set(p_instance->p_reg, channel,
169                                   p_config->count_mode == NRF_PWM_MODE_UP ?
170                                   !p_config->pin_inverted[channel]        :
171                                   p_config->pin_inverted[channel]);
172     }
173 #endif
174 
175 #if defined(USE_DMA_ISSUE_WORKAROUND)
176     apply_errata_109(p_config);
177 #endif
178 }
179 
pwm_stopped_check(nrfx_pwm_t const * p_instance)180 static bool pwm_stopped_check(nrfx_pwm_t const * p_instance)
181 {
182     pwm_control_block_t * p_cb  = &m_cb[p_instance->instance_id];
183 
184     if (!p_cb->handler)
185     {
186         if (nrfy_pwm_events_process(p_instance->p_reg,
187                                     NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_STOPPED)))
188         {
189             p_cb->state = NRFX_DRV_STATE_INITIALIZED;
190         }
191     }
192 
193     return p_cb->state != NRFX_DRV_STATE_POWERED_ON;
194 }
195 
nrfx_pwm_init(nrfx_pwm_t const * p_instance,nrfx_pwm_config_t const * p_config,nrfx_pwm_handler_t handler,void * p_context)196 nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const *        p_instance,
197                          nrfx_pwm_config_t const * p_config,
198                          nrfx_pwm_handler_t        handler,
199                          void *                    p_context)
200 {
201     nrfx_err_t err_code;
202 
203     pwm_control_block_t * p_cb  = &m_cb[p_instance->instance_id];
204 
205     if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
206     {
207 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
208         err_code = NRFX_ERROR_ALREADY;
209 #else
210         err_code = NRFX_ERROR_INVALID_STATE;
211 #endif
212         NRFX_LOG_WARNING("Function: %s, error code: %s.",
213                          __func__,
214                          NRFX_LOG_ERROR_STRING_GET(err_code));
215         return err_code;
216     }
217 
218     p_cb->handler = handler;
219     p_cb->p_context = p_context;
220 
221     if (p_config)
222     {
223         p_cb->skip_gpio_cfg = p_config->skip_gpio_cfg;
224         pwm_configure(p_instance, p_config);
225     }
226 
227     p_cb->state = NRFX_DRV_STATE_INITIALIZED;
228 
229     err_code = NRFX_SUCCESS;
230     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
231     return err_code;
232 }
233 
nrfx_pwm_reconfigure(nrfx_pwm_t const * p_instance,nrfx_pwm_config_t const * p_config)234 nrfx_err_t nrfx_pwm_reconfigure(nrfx_pwm_t const * p_instance, nrfx_pwm_config_t const * p_config)
235 {
236     pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
237 
238     NRFX_ASSERT(p_config);
239 
240     if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
241     {
242         return NRFX_ERROR_INVALID_STATE;
243     }
244     if (p_cb->state == NRFX_DRV_STATE_POWERED_ON)
245     {
246         return NRFX_ERROR_BUSY;
247     }
248 
249     pwm_configure(p_instance, p_config);
250 
251     return NRFX_SUCCESS;
252 }
253 
nrfx_pwm_uninit(nrfx_pwm_t const * p_instance)254 void nrfx_pwm_uninit(nrfx_pwm_t const * p_instance)
255 {
256     pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
257 
258     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
259 
260     nrfy_pwm_int_uninit(p_instance->p_reg);
261 #if defined(USE_DMA_ISSUE_WORKAROUND)
262     NRFY_IRQ_DISABLE(DMA_ISSUE_EGU_IRQn);
263 #endif
264 
265     nrfy_pwm_disable(p_instance->p_reg);
266 
267     if (!p_cb->skip_gpio_cfg)
268     {
269         pins_deconfigure(p_instance);
270     }
271 
272     p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
273     NRFX_LOG_INFO("Uninitialized.");
274 }
275 
nrfx_pwm_init_check(nrfx_pwm_t const * p_instance)276 bool nrfx_pwm_init_check(nrfx_pwm_t const * p_instance)
277 {
278     pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
279 
280     return (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
281 }
282 
start_playback(nrfx_pwm_t const * p_instance,pwm_control_block_t * p_cb,uint32_t flags,uint8_t seq_id)283 static uint32_t start_playback(nrfx_pwm_t const *    p_instance,
284                                pwm_control_block_t * p_cb,
285                                uint32_t              flags,
286                                uint8_t               seq_id)
287 {
288     p_cb->state = NRFX_DRV_STATE_POWERED_ON;
289     p_cb->flags = flags;
290 
291     nrfy_pwm_enable(p_instance->p_reg);
292 
293     if (p_cb->handler)
294     {
295         // The notification about finished playback is by default enabled,
296         // but this can be suppressed.
297         // The notification that the peripheral has stopped is always enabled.
298         uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
299                             NRF_PWM_INT_STOPPED_MASK;
300 
301         // The workaround for nRF52 Anomaly 109 "protects" DMA transfers by
302         // handling interrupts generated on SEQEND0 and SEQEND1 events (see
303         // 'nrfx_pwm_init'), hence these events must be always enabled
304         // to generate interrupts.
305         // However, the user handler is called for them only when requested
306         // (see 'irq_handler').
307 #if defined(USE_DMA_ISSUE_WORKAROUND)
308         int_mask |= NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK;
309 #else
310         if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0)
311         {
312             int_mask |= NRF_PWM_INT_SEQEND0_MASK;
313         }
314         if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1)
315         {
316             int_mask |= NRF_PWM_INT_SEQEND1_MASK;
317         }
318 #endif
319         if (flags & NRFX_PWM_FLAG_NO_EVT_FINISHED)
320         {
321             int_mask &= (uint32_t)~NRF_PWM_INT_LOOPSDONE_MASK;
322         }
323 
324         nrfy_pwm_int_set(p_instance->p_reg, int_mask);
325     }
326 #if defined(USE_DMA_ISSUE_WORKAROUND)
327     else
328     {
329         nrfy_pwm_int_set(p_instance->p_reg,
330             NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK);
331     }
332 #endif
333     if (flags & NRFX_PWM_FLAG_START_VIA_TASK)
334     {
335         uint32_t starting_task_address =
336             nrfy_pwm_task_address_get(p_instance->p_reg, nrfy_pwm_seqstart_task_get(seq_id));
337 
338 #if defined(USE_DMA_ISSUE_WORKAROUND)
339         // To "protect" the initial DMA transfer it is required to start
340         // the PWM by triggering the proper task from EGU interrupt handler,
341         // it is not safe to do it directly via PPI.
342         p_cb->starting_task_address = starting_task_address;
343         nrf_egu_int_enable(DMA_ISSUE_EGU, nrf_egu_channel_int_get(p_instance->instance_id));
344         return nrf_egu_task_address_get(DMA_ISSUE_EGU,
345                                         nrf_egu_trigger_task_get(p_instance->instance_id));
346 #else
347         return starting_task_address;
348 #endif
349     }
350 
351     nrfy_pwm_start(p_instance->p_reg, seq_id, false);
352     return 0;
353 }
354 
nrfx_pwm_simple_playback(nrfx_pwm_t const * p_instance,nrf_pwm_sequence_t const * p_sequence,uint16_t playback_count,uint32_t flags)355 uint32_t nrfx_pwm_simple_playback(nrfx_pwm_t const *         p_instance,
356                                   nrf_pwm_sequence_t const * p_sequence,
357                                   uint16_t                   playback_count,
358                                   uint32_t                   flags)
359 {
360     pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
361 
362     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
363     NRFX_ASSERT(playback_count > 0);
364     NRFX_ASSERT(nrfx_is_in_ram(p_sequence->values.p_raw));
365 
366     // To take advantage of the looping mechanism, we need to use both sequences
367     // (single sequence can be played back only once).
368     nrfy_pwm_sequence_set(p_instance->p_reg, 0, p_sequence);
369     nrfy_pwm_sequence_set(p_instance->p_reg, 1, p_sequence);
370     bool odd = (playback_count & 1);
371     nrfy_pwm_loop_set(p_instance->p_reg,
372         (uint16_t)((playback_count / 2UL) + (odd ? 1UL : 0UL)));
373 
374     uint32_t shorts_mask = 0;
375     if (flags & NRFX_PWM_FLAG_STOP)
376     {
377         shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
378     }
379     else if (flags & NRFX_PWM_FLAG_LOOP)
380     {
381 #if NRF_PWM_HAS_SHORT_LOOPSDONE_SEQSTART
382         shorts_mask = odd ? NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK
383                           : NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
384 #else
385         NRFX_ASSERT(0);
386 #endif
387     }
388     nrfy_pwm_shorts_set(p_instance->p_reg, shorts_mask);
389 
390     NRFX_LOG_INFO("Function: %s, sequence length: %d.",
391                   __func__,
392                   p_sequence->length);
393     NRFX_LOG_DEBUG("Sequence data:");
394     NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_sequence->values.p_raw,
395                            p_sequence->length * sizeof(uint16_t));
396     return start_playback(p_instance, p_cb, flags, odd ? 1 : 0);
397 }
398 
nrfx_pwm_complex_playback(nrfx_pwm_t const * p_instance,nrf_pwm_sequence_t const * p_sequence_0,nrf_pwm_sequence_t const * p_sequence_1,uint16_t playback_count,uint32_t flags)399 uint32_t nrfx_pwm_complex_playback(nrfx_pwm_t const *         p_instance,
400                                    nrf_pwm_sequence_t const * p_sequence_0,
401                                    nrf_pwm_sequence_t const * p_sequence_1,
402                                    uint16_t                   playback_count,
403                                    uint32_t                   flags)
404 {
405     pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
406 
407     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
408     NRFX_ASSERT(playback_count > 0);
409     NRFX_ASSERT(nrfx_is_in_ram(p_sequence_0->values.p_raw));
410     NRFX_ASSERT(nrfx_is_in_ram(p_sequence_1->values.p_raw));
411 
412     nrfy_pwm_sequence_set(p_instance->p_reg, 0, p_sequence_0);
413     nrfy_pwm_sequence_set(p_instance->p_reg, 1, p_sequence_1);
414     nrfy_pwm_loop_set(p_instance->p_reg, playback_count);
415 
416     uint32_t shorts_mask = 0;
417     if (flags & NRFX_PWM_FLAG_STOP)
418     {
419         shorts_mask = NRF_PWM_SHORT_LOOPSDONE_STOP_MASK;
420     }
421     else if (flags & NRFX_PWM_FLAG_LOOP)
422     {
423 #if NRF_PWM_HAS_SHORT_LOOPSDONE_SEQSTART
424         shorts_mask = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK;
425 #else
426         NRFX_ASSERT(0);
427 #endif
428     }
429     nrfy_pwm_shorts_set(p_instance->p_reg, shorts_mask);
430 
431     NRFX_LOG_INFO("Function: %s, sequence 0 length: %d.",
432                   __func__,
433                   p_sequence_0->length);
434     NRFX_LOG_INFO("Function: %s, sequence 1 length: %d.",
435                   __func__,
436                   p_sequence_1->length);
437     NRFX_LOG_DEBUG("Sequence 0 data:");
438     NRFX_LOG_HEXDUMP_DEBUG(p_sequence_0->values.p_raw,
439                            p_sequence_0->length * sizeof(uint16_t));
440     NRFX_LOG_DEBUG("Sequence 1 data:");
441     NRFX_LOG_HEXDUMP_DEBUG(p_sequence_1->values.p_raw,
442                            p_sequence_1->length * sizeof(uint16_t));
443     return start_playback(p_instance, p_cb, flags, 0);
444 }
445 
nrfx_pwm_stop(nrfx_pwm_t const * p_instance,bool wait_until_stopped)446 bool nrfx_pwm_stop(nrfx_pwm_t const * p_instance, bool wait_until_stopped)
447 {
448     pwm_control_block_t * p_cb = &m_cb[p_instance->instance_id];
449 
450     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
451 
452     bool ret_val = false;
453 
454     // Deactivate shortcuts before triggering the STOP task, otherwise the PWM
455     // could be immediately started again if the LOOPSDONE event occurred in
456     // the same peripheral clock cycle as the STOP task was triggered.
457     nrfy_pwm_shorts_set(p_instance->p_reg, 0);
458 
459     // Trigger the STOP task even if the PWM appears to be already stopped.
460     // It won't harm, but it could help if for some strange reason the stopped
461     // status was not reported correctly.
462     nrfy_pwm_abort(p_instance->p_reg, false);
463 
464     if (wait_until_stopped)
465     {
466         while (!pwm_stopped_check(p_instance))
467         {}
468         nrfy_pwm_disable(p_instance->p_reg);
469         p_cb->state = NRFX_DRV_STATE_INITIALIZED;
470     }
471 
472     ret_val = pwm_stopped_check(p_instance);
473 
474     NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
475     return ret_val;
476 }
477 
nrfx_pwm_stopped_check(nrfx_pwm_t const * p_instance)478 bool nrfx_pwm_stopped_check(nrfx_pwm_t const * p_instance)
479 {
480     NRFX_ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED);
481 
482     bool ret_val = pwm_stopped_check(p_instance);
483 
484     NRFX_LOG_INFO("%s returned %d.", __func__, ret_val);
485     return ret_val;
486 }
487 
irq_handler(NRF_PWM_Type * p_pwm,pwm_control_block_t * p_cb)488 static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
489 {
490     uint32_t evt_mask = nrfy_pwm_events_process(p_pwm,
491                                                 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND0)   |
492                                                 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND1)   |
493                                                 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_LOOPSDONE) |
494                                                 NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_STOPPED));
495 
496     // The user handler is called for SEQEND0 and SEQEND1 events only when the
497     // user asks for it (by setting proper flags when starting the playback).
498     if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND0))
499     {
500         if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0) && p_cb->handler)
501         {
502             p_cb->handler(NRFX_PWM_EVT_END_SEQ0, p_cb->p_context);
503         }
504     }
505     if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_SEQEND1))
506     {
507         if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1) && p_cb->handler)
508         {
509             p_cb->handler(NRFX_PWM_EVT_END_SEQ1, p_cb->p_context);
510         }
511     }
512     // For LOOPSDONE the handler is called by default, but the user can disable
513     // this (via flags).
514     if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_LOOPSDONE))
515     {
516         if (!(p_cb->flags & NRFX_PWM_FLAG_NO_EVT_FINISHED) && p_cb->handler)
517         {
518             p_cb->handler(NRFX_PWM_EVT_FINISHED, p_cb->p_context);
519         }
520     }
521 
522     // The STOPPED event is always propagated to the user handler.
523     if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_PWM_EVENT_STOPPED))
524     {
525         nrfy_pwm_disable(p_pwm);
526         p_cb->state = NRFX_DRV_STATE_INITIALIZED;
527         if (p_cb->handler)
528         {
529             p_cb->handler(NRFX_PWM_EVT_STOPPED, p_cb->p_context);
530         }
531     }
532 }
533 
534 #if defined(USE_DMA_ISSUE_WORKAROUND)
535 // See 'start_playback' why this is needed.
DMA_ISSUE_EGU_IRQHandler(void)536 void DMA_ISSUE_EGU_IRQHandler(void)
537 {
538     for (uint8_t i = 0; i < NRFX_PWM_ENABLED_COUNT; i++)
539     {
540         nrf_egu_event_t event = nrf_egu_triggered_event_get(i);
541         if (nrf_egu_event_check(DMA_ISSUE_EGU, event))
542         {
543             nrf_egu_event_clear(DMA_ISSUE_EGU, event);
544             *(volatile uint32_t *)(m_cb[i].starting_task_address) = 1;
545         }
546     }
547 }
548 #endif
549 
550 NRFX_INSTANCE_IRQ_HANDLERS(PWM, pwm)
551 
552 #endif // NRFX_CHECK(NRFX_PWM_ENABLED)
553