dma.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#ifndef _HARDWARE_DMA_H_
8#define _HARDWARE_DMA_H_
9
10#include "pico.h"
11#include "hardware/structs/dma.h"
12#include "hardware/regs/dreq.h"
13#include "pico/assert.h"
14
15#ifdef __cplusplus
16extern "C" {
17#endif
18
37// these are not defined in generated dreq.h
38#define DREQ_DMA_TIMER0 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER0
39#define DREQ_DMA_TIMER1 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER1
40#define DREQ_DMA_TIMER2 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER2
41#define DREQ_DMA_TIMER3 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER3
42#define DREQ_FORCE DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_PERMANENT
43
44// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_DMA, Enable/disable DMA assertions, type=bool, default=0, group=hardware_dma
45#ifndef PARAM_ASSERTIONS_ENABLED_DMA
46#define PARAM_ASSERTIONS_ENABLED_DMA 0
47#endif
48
49static inline void check_dma_channel_param(__unused uint channel) {
50#if PARAM_ASSERTIONS_ENABLED(DMA)
51 // this method is used a lot by inline functions so avoid code bloat by deferring to function
52 extern void check_dma_channel_param_impl(uint channel);
53 check_dma_channel_param_impl(channel);
54#endif
55}
56
57static inline void check_dma_timer_param(__unused uint timer_num) {
58 valid_params_if(DMA, timer_num < NUM_DMA_TIMERS);
59}
60
61inline static dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
62 check_dma_channel_param(channel);
63 return &dma_hw->ch[channel];
64}
65
75void dma_channel_claim(uint channel);
76
86void dma_claim_mask(uint32_t channel_mask);
87
95void dma_channel_unclaim(uint channel);
96
103int dma_claim_unused_channel(bool required);
104
113bool dma_channel_is_claimed(uint channel);
114
132 DMA_SIZE_32 = 2
134
135typedef struct {
136 uint32_t ctrl;
138
146static inline void channel_config_set_read_increment(dma_channel_config *c, bool incr) {
147 c->ctrl = incr ? (c->ctrl | DMA_CH0_CTRL_TRIG_INCR_READ_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_INCR_READ_BITS);
148}
149
159 c->ctrl = incr ? (c->ctrl | DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS);
160}
161
177static inline void channel_config_set_dreq(dma_channel_config *c, uint dreq) {
178 assert(dreq <= DREQ_FORCE);
179 c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_TREQ_SEL_BITS) | (dreq << DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB);
180}
181
191static inline void channel_config_set_chain_to(dma_channel_config *c, uint chain_to) {
192 assert(chain_to <= NUM_DMA_CHANNELS);
193 c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (chain_to << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
194}
195
206 assert(size == DMA_SIZE_8 || size == DMA_SIZE_16 || size == DMA_SIZE_32);
207 c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) | (((uint)size) << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB);
208}
209
225static inline void channel_config_set_ring(dma_channel_config *c, bool write, uint size_bits) {
226 assert(size_bits < 32);
227 c->ctrl = (c->ctrl & ~(DMA_CH0_CTRL_TRIG_RING_SIZE_BITS | DMA_CH0_CTRL_TRIG_RING_SEL_BITS)) |
228 (size_bits << DMA_CH0_CTRL_TRIG_RING_SIZE_LSB) |
229 (write ? DMA_CH0_CTRL_TRIG_RING_SEL_BITS : 0);
230}
231
241static inline void channel_config_set_bswap(dma_channel_config *c, bool bswap) {
242 c->ctrl = bswap ? (c->ctrl | DMA_CH0_CTRL_TRIG_BSWAP_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_BSWAP_BITS);
243}
244
255static inline void channel_config_set_irq_quiet(dma_channel_config *c, bool irq_quiet) {
256 c->ctrl = irq_quiet ? (c->ctrl | DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS);
257}
258
270static inline void channel_config_set_enable(dma_channel_config *c, bool enable) {
271 c->ctrl = enable ? (c->ctrl | DMA_CH0_CTRL_TRIG_EN_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_EN_BITS);
272}
273
282static inline void channel_config_set_sniff_enable(dma_channel_config *c, bool sniff_enable) {
283 c->ctrl = sniff_enable ? (c->ctrl | DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS) : (c->ctrl &
284 ~DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS);
285}
286
307 dma_channel_config c = {0};
310 channel_config_set_dreq(&c, DREQ_FORCE);
311 channel_config_set_chain_to(&c, channel);
313 channel_config_set_ring(&c, false, 0);
314 channel_config_set_bswap(&c, false);
318 return c;
319}
320
327static inline dma_channel_config dma_get_channel_config(uint channel) {
329 c.ctrl = dma_channel_hw_addr(channel)->ctrl_trig;
330 return c;
331}
332
339static inline uint32_t channel_config_get_ctrl_value(const dma_channel_config *config) {
340 return config->ctrl;
341}
342
350static inline void dma_channel_set_config(uint channel, const dma_channel_config *config, bool trigger) {
351 // Don't use CTRL_TRIG since we don't want to start a transfer
352 if (!trigger) {
353 dma_channel_hw_addr(channel)->al1_ctrl = channel_config_get_ctrl_value(config);
354 } else {
355 dma_channel_hw_addr(channel)->ctrl_trig = channel_config_get_ctrl_value(config);
356 }
357}
358
366static inline void dma_channel_set_read_addr(uint channel, const volatile void *read_addr, bool trigger) {
367 if (!trigger) {
368 dma_channel_hw_addr(channel)->read_addr = (uintptr_t) read_addr;
369 } else {
370 dma_channel_hw_addr(channel)->al3_read_addr_trig = (uintptr_t) read_addr;
371 }
372}
373
381static inline void dma_channel_set_write_addr(uint channel, volatile void *write_addr, bool trigger) {
382 if (!trigger) {
383 dma_channel_hw_addr(channel)->write_addr = (uintptr_t) write_addr;
384 } else {
385 dma_channel_hw_addr(channel)->al2_write_addr_trig = (uintptr_t) write_addr;
386 }
387}
388
396static inline void dma_channel_set_trans_count(uint channel, uint32_t trans_count, bool trigger) {
397 if (!trigger) {
398 dma_channel_hw_addr(channel)->transfer_count = trans_count;
399 } else {
400 dma_channel_hw_addr(channel)->al1_transfer_count_trig = trans_count;
401 }
402}
403
414static inline void dma_channel_configure(uint channel, const dma_channel_config *config, volatile void *write_addr,
415 const volatile void *read_addr,
416 uint transfer_count, bool trigger) {
417 dma_channel_set_read_addr(channel, read_addr, false);
418 dma_channel_set_write_addr(channel, write_addr, false);
419 dma_channel_set_trans_count(channel, transfer_count, false);
420 dma_channel_set_config(channel, config, trigger);
421}
422
430inline static void __attribute__((always_inline)) dma_channel_transfer_from_buffer_now(uint channel,
431 const volatile void *read_addr,
432 uint32_t transfer_count) {
433// check_dma_channel_param(channel);
434 dma_channel_hw_t *hw = dma_channel_hw_addr(channel);
435 hw->read_addr = (uintptr_t) read_addr;
436 hw->al1_transfer_count_trig = transfer_count;
437}
438
446inline static void dma_channel_transfer_to_buffer_now(uint channel, volatile void *write_addr, uint32_t transfer_count) {
447 dma_channel_hw_t *hw = dma_channel_hw_addr(channel);
448 hw->write_addr = (uintptr_t) write_addr;
449 hw->al1_transfer_count_trig = transfer_count;
450}
451
457static inline void dma_start_channel_mask(uint32_t chan_mask) {
458 valid_params_if(DMA, chan_mask && chan_mask < (1u << NUM_DMA_CHANNELS));
459 dma_hw->multi_channel_trigger = chan_mask;
460}
461
467static inline void dma_channel_start(uint channel) {
468 dma_start_channel_mask(1u << channel);
469}
470
478static inline void dma_channel_abort(uint channel) {
479 check_dma_channel_param(channel);
480 dma_hw->abort = 1u << channel;
481 // Bit will go 0 once channel has reached safe state
482 // (i.e. any in-flight transfers have retired)
483 while (dma_hw->abort & (1ul << channel)) tight_loop_contents();
484}
485
492static inline void dma_channel_set_irq0_enabled(uint channel, bool enabled) {
493 check_dma_channel_param(channel);
494 check_hw_layout(dma_hw_t, inte0, DMA_INTE0_OFFSET);
495 if (enabled)
496 hw_set_bits(&dma_hw->inte0, 1u << channel);
497 else
498 hw_clear_bits(&dma_hw->inte0, 1u << channel);
499}
500
507static inline void dma_set_irq0_channel_mask_enabled(uint32_t channel_mask, bool enabled) {
508 if (enabled) {
509 hw_set_bits(&dma_hw->inte0, channel_mask);
510 } else {
511 hw_clear_bits(&dma_hw->inte0, channel_mask);
512 }
513}
514
521static inline void dma_channel_set_irq1_enabled(uint channel, bool enabled) {
522 check_dma_channel_param(channel);
523 check_hw_layout(dma_hw_t, inte1, DMA_INTE1_OFFSET);
524 if (enabled)
525 hw_set_bits(&dma_hw->inte1, 1u << channel);
526 else
527 hw_clear_bits(&dma_hw->inte1, 1u << channel);
528}
529
536static inline void dma_set_irq1_channel_mask_enabled(uint32_t channel_mask, bool enabled) {
537 if (enabled) {
538 hw_set_bits(&dma_hw->inte1, channel_mask);
539 } else {
540 hw_clear_bits(&dma_hw->inte1, channel_mask);
541 }
542}
543
551static inline void dma_irqn_set_channel_enabled(uint irq_index, uint channel, bool enabled) {
552 invalid_params_if(DMA, irq_index > 1);
553 if (irq_index) {
554 dma_channel_set_irq1_enabled(channel, enabled);
555 } else {
556 dma_channel_set_irq0_enabled(channel, enabled);
557 }
558}
559
567static inline void dma_irqn_set_channel_mask_enabled(uint irq_index, uint32_t channel_mask, bool enabled) {
568 invalid_params_if(DMA, irq_index > 1);
569 if (irq_index) {
570 dma_set_irq1_channel_mask_enabled(channel_mask, enabled);
571 } else {
572 dma_set_irq0_channel_mask_enabled(channel_mask, enabled);
573 }
574}
575
582static inline bool dma_channel_get_irq0_status(uint channel) {
583 check_dma_channel_param(channel);
584 return dma_hw->ints0 & (1u << channel);
585}
586
593static inline bool dma_channel_get_irq1_status(uint channel) {
594 check_dma_channel_param(channel);
595 return dma_hw->ints1 & (1u << channel);
596}
597
605static inline bool dma_irqn_get_channel_status(uint irq_index, uint channel) {
606 invalid_params_if(DMA, irq_index > 1);
607 check_dma_channel_param(channel);
608 return (irq_index ? dma_hw->ints1 : dma_hw->ints0) & (1u << channel);
609}
610
616static inline void dma_channel_acknowledge_irq0(uint channel) {
617 check_dma_channel_param(channel);
618 hw_set_bits(&dma_hw->ints0, (1u << channel));
619}
620
626static inline void dma_channel_acknowledge_irq1(uint channel) {
627 check_dma_channel_param(channel);
628 hw_set_bits(&dma_hw->ints1, (1u << channel));
629}
630
637static inline void dma_irqn_acknowledge_channel(uint irq_index, uint channel) {
638 invalid_params_if(DMA, irq_index > 1);
639 check_dma_channel_param(channel);
640 hw_set_bits(irq_index ? &dma_hw->ints1 : &dma_hw->ints0, (1u << channel));
641}
642
649inline static bool dma_channel_is_busy(uint channel) {
650 check_dma_channel_param(channel);
651 return !!(dma_hw->ch[channel].al1_ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS);
652}
653
659inline static void dma_channel_wait_for_finish_blocking(uint channel) {
660 while (dma_channel_is_busy(channel)) tight_loop_contents();
661 // stop the compiler hoisting a non volatile buffer access above the DMA completion.
663}
664
685inline static void dma_sniffer_enable(uint channel, uint mode, bool force_channel_enable) {
686 check_dma_channel_param(channel);
687 check_hw_layout(dma_hw_t, sniff_ctrl, DMA_SNIFF_CTRL_OFFSET);
688 if (force_channel_enable) {
689 hw_set_bits(&dma_hw->ch[channel].al1_ctrl, DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS);
690 }
691 dma_hw->sniff_ctrl = ((channel << DMA_SNIFF_CTRL_DMACH_LSB) & DMA_SNIFF_CTRL_DMACH_BITS) |
692 ((mode << DMA_SNIFF_CTRL_CALC_LSB) & DMA_SNIFF_CTRL_CALC_BITS) |
693 DMA_SNIFF_CTRL_EN_BITS;
694}
695
707inline static void dma_sniffer_set_byte_swap_enabled(bool swap) {
708 if (swap)
709 hw_set_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_BSWAP_BITS);
710 else
711 hw_clear_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_BSWAP_BITS);
712}
713
718inline static void dma_sniffer_disable(void) {
719 dma_hw->sniff_ctrl = 0;
720}
721
731void dma_timer_claim(uint timer);
732
740void dma_timer_unclaim(uint timer);
741
748int dma_claim_unused_timer(bool required);
749
757bool dma_timer_is_claimed(uint timer);
758
769static inline void dma_timer_set_fraction(uint timer, uint16_t numerator, uint16_t denominator) {
770 check_dma_timer_param(timer);
771 dma_hw->timer[timer] = (((uint32_t)numerator) << DMA_TIMER0_X_LSB) | (((uint32_t)denominator) << DMA_TIMER0_Y_LSB);
772}
773
779static inline uint dma_get_timer_dreq(uint timer_num) {
780 static_assert(DREQ_DMA_TIMER1 == DREQ_DMA_TIMER0 + 1, "");
781 static_assert(DREQ_DMA_TIMER2 == DREQ_DMA_TIMER0 + 2, "");
782 static_assert(DREQ_DMA_TIMER3 == DREQ_DMA_TIMER0 + 3, "");
783 check_dma_timer_param(timer_num);
784 return DREQ_DMA_TIMER0 + timer_num;
785}
786
787#ifndef NDEBUG
788void print_dma_ctrl(dma_channel_hw_t *channel);
789#endif
790
791#ifdef __cplusplus
792}
793#endif
794
795#endif
static void channel_config_set_read_increment(dma_channel_config *c, bool incr)
Set DMA channel read increment.
Definition: dma.h:146
static dma_channel_config dma_get_channel_config(uint channel)
Get the current configuration for the specified channel.
Definition: dma.h:327
static dma_channel_config dma_channel_get_default_config(uint channel)
Get the default channel configuration for a given channel.
Definition: dma.h:306
static void channel_config_set_bswap(dma_channel_config *c, bool bswap)
Set DMA byte swapping.
Definition: dma.h:241
static void channel_config_set_sniff_enable(dma_channel_config *c, bool sniff_enable)
Enable access to channel by sniff hardware.
Definition: dma.h:282
static void channel_config_set_dreq(dma_channel_config *c, uint dreq)
Select a transfer request signal.
Definition: dma.h:177
static void channel_config_set_chain_to(dma_channel_config *c, uint chain_to)
Set DMA channel completion channel.
Definition: dma.h:191
static uint32_t channel_config_get_ctrl_value(const dma_channel_config *config)
Get the raw configuration register from a channel configuration.
Definition: dma.h:339
static void channel_config_set_write_increment(dma_channel_config *c, bool incr)
Set DMA channel write increment.
Definition: dma.h:158
static void channel_config_set_irq_quiet(dma_channel_config *c, bool irq_quiet)
Set IRQ quiet mode.
Definition: dma.h:255
static void channel_config_set_enable(dma_channel_config *c, bool enable)
Enable/Disable the DMA channel.
Definition: dma.h:270
static void channel_config_set_transfer_data_size(dma_channel_config *c, enum dma_channel_transfer_size size)
Set the size of each DMA bus transfer.
Definition: dma.h:205
static void channel_config_set_ring(dma_channel_config *c, bool write, uint size_bits)
Set address wrapping parameters.
Definition: dma.h:225
static __force_inline void hw_set_bits(io_rw_32 *addr, uint32_t mask)
Atomically set the specified bits to 1 in a HW register.
Definition: address_mapped.h:121
static __force_inline void hw_clear_bits(io_rw_32 *addr, uint32_t mask)
Atomically clear the specified bits to 0 in a HW register.
Definition: address_mapped.h:131
static void dma_sniffer_enable(uint channel, uint mode, bool force_channel_enable)
Enable the DMA sniffing targeting the specified channel.
Definition: dma.h:685
void dma_timer_claim(uint timer)
Mark a dma timer as used.
Definition: dma.c:48
static void dma_channel_set_trans_count(uint channel, uint32_t trans_count, bool trigger)
Set the number of bus transfers the channel will do.
Definition: dma.h:396
int dma_claim_unused_timer(bool required)
Claim a free dma timer.
Definition: dma.c:58
static void dma_channel_start(uint channel)
Start a single DMA channel.
Definition: dma.h:467
static bool dma_irqn_get_channel_status(uint irq_index, uint channel)
Determine if a particular channel is a cause of DMA_IRQ_N.
Definition: dma.h:605
int dma_claim_unused_channel(bool required)
Claim a free dma channel.
Definition: dma.c:39
static void dma_channel_acknowledge_irq1(uint channel)
Acknowledge a channel IRQ, resetting it as the cause of DMA_IRQ_1.
Definition: dma.h:626
static void dma_channel_set_irq1_enabled(uint channel, bool enabled)
Enable single DMA channel's interrupt via DMA_IRQ_1.
Definition: dma.h:521
static void dma_timer_set_fraction(uint timer, uint16_t numerator, uint16_t denominator)
Set the divider for the given DMA timer.
Definition: dma.h:769
static void dma_start_channel_mask(uint32_t chan_mask)
Start one or more channels simultaneously.
Definition: dma.h:457
static void dma_irqn_set_channel_enabled(uint irq_index, uint channel, bool enabled)
Enable single DMA channel interrupt on either DMA_IRQ_0 or DMA_IRQ_1.
Definition: dma.h:551
static void dma_irqn_set_channel_mask_enabled(uint irq_index, uint32_t channel_mask, bool enabled)
Enable multiple DMA channels' interrupt via either DMA_IRQ_0 or DMA_IRQ_1.
Definition: dma.h:567
static void dma_channel_abort(uint channel)
Stop a DMA transfer.
Definition: dma.h:478
static void dma_channel_set_config(uint channel, const dma_channel_config *config, bool trigger)
Set a channel configuration.
Definition: dma.h:350
static uint dma_get_timer_dreq(uint timer_num)
Return the DREQ number for a given DMA timer.
Definition: dma.h:779
void dma_timer_unclaim(uint timer)
Mark a dma timer as no longer used.
Definition: dma.c:53
static void dma_channel_transfer_to_buffer_now(uint channel, volatile void *write_addr, uint32_t transfer_count)
Start a DMA transfer to a buffer immediately.
Definition: dma.h:446
static void dma_channel_configure(uint channel, const dma_channel_config *config, volatile void *write_addr, const volatile void *read_addr, uint transfer_count, bool trigger)
Configure all DMA parameters and optionally start transfer.
Definition: dma.h:414
bool dma_channel_is_claimed(uint channel)
Determine if a dma channel is claimed.
Definition: dma.c:43
bool dma_timer_is_claimed(uint timer)
Determine if a dma timer is claimed.
Definition: dma.c:62
static void dma_sniffer_disable(void)
Disable the DMA sniffer.
Definition: dma.h:718
void dma_claim_mask(uint32_t channel_mask)
Mark multiple dma channels as used.
Definition: dma.c:28
static bool dma_channel_get_irq0_status(uint channel)
Determine if a particular channel is a cause of DMA_IRQ_0.
Definition: dma.h:582
static void dma_set_irq1_channel_mask_enabled(uint32_t channel_mask, bool enabled)
Enable multiple DMA channels' interrupts via DMA_IRQ_1.
Definition: dma.h:536
static void dma_channel_wait_for_finish_blocking(uint channel)
Wait for a DMA channel transfer to complete.
Definition: dma.h:659
static void dma_channel_transfer_from_buffer_now(uint channel, const volatile void *read_addr, uint32_t transfer_count)
Start a DMA transfer from a buffer immediately.
Definition: dma.h:430
static void dma_channel_set_read_addr(uint channel, const volatile void *read_addr, bool trigger)
Set the DMA initial read address.
Definition: dma.h:366
static void dma_sniffer_set_byte_swap_enabled(bool swap)
Enable the Sniffer byte swap function.
Definition: dma.h:707
void dma_channel_unclaim(uint channel)
Mark a dma channel as no longer used.
Definition: dma.c:34
dma_channel_transfer_size
Enumeration of available DMA channel transfer sizes.
Definition: dma.h:129
static void dma_irqn_acknowledge_channel(uint irq_index, uint channel)
Acknowledge a channel IRQ, resetting it as the cause of DMA_IRQ_N.
Definition: dma.h:637
static void dma_set_irq0_channel_mask_enabled(uint32_t channel_mask, bool enabled)
Enable multiple DMA channels' interrupts via DMA_IRQ_0.
Definition: dma.h:507
void dma_channel_claim(uint channel)
Mark a dma channel as used.
Definition: dma.c:23
static void dma_channel_set_write_addr(uint channel, volatile void *write_addr, bool trigger)
Set the DMA initial write address.
Definition: dma.h:381
static void dma_channel_set_irq0_enabled(uint channel, bool enabled)
Enable single DMA channel's interrupt via DMA_IRQ_0.
Definition: dma.h:492
static bool dma_channel_get_irq1_status(uint channel)
Determine if a particular channel is a cause of DMA_IRQ_1.
Definition: dma.h:593
static bool dma_channel_is_busy(uint channel)
Check if DMA channel is busy.
Definition: dma.h:649
static void dma_channel_acknowledge_irq0(uint channel)
Acknowledge a channel IRQ, resetting it as the cause of DMA_IRQ_0.
Definition: dma.h:616
@ DMA_SIZE_16
Half word transfer (16 bits)
Definition: dma.h:131
@ DMA_SIZE_8
Byte transfer (8 bits)
Definition: dma.h:130
@ DMA_SIZE_32
Word transfer (32 bits)
Definition: dma.h:132
static __always_inline void __compiler_memory_barrier(void)
Ensure that the compiler does not move memory access across this method call.
Definition: platform.h:282
static __always_inline void tight_loop_contents(void)
No-op function for the body of tight loops.
Definition: platform.h:347
Definition: dma.h:135
Definition: dma.h:23
Definition: dma.h:105