1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdio.h>
8 #include "hardware/dma.h"
9 #include "hardware/claim.h"
10 
11 #define DMA_CHAN_STRIDE (DMA_CH1_CTRL_TRIG_OFFSET - DMA_CH0_CTRL_TRIG_OFFSET)
12 check_hw_size(dma_channel_hw_t, DMA_CHAN_STRIDE);
13 check_hw_layout(dma_hw_t, abort, DMA_CHAN_ABORT_OFFSET);
14 
15 // sanity check
16 static_assert(offsetof(dma_hw_t, ch[0].ctrl_trig) == DMA_CH0_CTRL_TRIG_OFFSET, "hw mismatch");
17 static_assert(offsetof(dma_hw_t, ch[1].ctrl_trig) == DMA_CH1_CTRL_TRIG_OFFSET, "hw mismatch");
18 
19 static_assert(NUM_DMA_CHANNELS <= 16, "");
20 static uint16_t _claimed;
21 static uint8_t _timer_claimed;
22 
dma_channel_claim(uint channel)23 void dma_channel_claim(uint channel) {
24     check_dma_channel_param(channel);
25     hw_claim_or_assert((uint8_t *) &_claimed, channel, "DMA channel %d is already claimed");
26 }
27 
dma_claim_mask(uint32_t mask)28 void dma_claim_mask(uint32_t mask) {
29     for(uint i = 0; mask; i++, mask >>= 1u) {
30         if (mask & 1u) dma_channel_claim(i);
31     }
32 }
33 
dma_channel_unclaim(uint channel)34 void dma_channel_unclaim(uint channel) {
35     check_dma_channel_param(channel);
36     hw_claim_clear((uint8_t *) &_claimed, channel);
37 }
38 
dma_unclaim_mask(uint32_t mask)39 void dma_unclaim_mask(uint32_t mask) {
40     for(uint i = 0; mask; i++, mask >>= 1u) {
41         if (mask & 1u) dma_channel_unclaim(i);
42     }
43 }
44 
dma_claim_unused_channel(bool required)45 int dma_claim_unused_channel(bool required) {
46     return hw_claim_unused_from_range((uint8_t*)&_claimed, required, 0, NUM_DMA_CHANNELS-1, "No DMA channels are available");
47 }
48 
dma_channel_is_claimed(uint channel)49 bool dma_channel_is_claimed(uint channel) {
50     check_dma_channel_param(channel);
51     return hw_is_claimed((uint8_t *) &_claimed, channel);
52 }
53 
dma_timer_claim(uint timer)54 void dma_timer_claim(uint timer) {
55     check_dma_timer_param(timer);
56     hw_claim_or_assert(&_timer_claimed, timer, "DMA timer %d is already claimed");
57 }
58 
dma_timer_unclaim(uint timer)59 void dma_timer_unclaim(uint timer) {
60     check_dma_timer_param(timer);
61     hw_claim_clear(&_timer_claimed, timer);
62 }
63 
dma_claim_unused_timer(bool required)64 int dma_claim_unused_timer(bool required) {
65     return hw_claim_unused_from_range(&_timer_claimed, required, 0, NUM_DMA_TIMERS-1, "No DMA timers are available");
66 }
67 
dma_timer_is_claimed(uint timer)68 bool dma_timer_is_claimed(uint timer) {
69     check_dma_timer_param(timer);
70     return hw_is_claimed(&_timer_claimed, timer);
71 }
72 
dma_channel_cleanup(uint channel)73 void dma_channel_cleanup(uint channel) {
74     check_dma_channel_param(channel);
75     // Disable CHAIN_TO, and disable channel, so that it ignores any further triggers
76     hw_write_masked( &dma_hw->ch[channel].al1_ctrl, (channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB) | (0u << DMA_CH0_CTRL_TRIG_EN_LSB), DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS | DMA_CH0_CTRL_TRIG_EN_BITS );
77     // disable IRQs first as abort can cause spurious IRQs
78     for(uint i=0; i < NUM_DMA_IRQS; i++) {
79         dma_irqn_set_channel_enabled(i, channel, false);
80     }
81     dma_channel_abort(channel);
82     // finally clear the IRQ status, which may have been set during abort
83     dma_hw->intr = 1u << channel;
84 }
85 
86 #ifndef NDEBUG
87 
print_dma_ctrl(dma_channel_hw_t * channel)88 void print_dma_ctrl(dma_channel_hw_t *channel) {
89     uint32_t ctrl = channel->ctrl_trig;
90     int rgsz = (ctrl & DMA_CH0_CTRL_TRIG_RING_SIZE_BITS) >> DMA_CH0_CTRL_TRIG_RING_SIZE_LSB;
91     printf("(%08x) ber %d rer %d wer %d busy %d trq %d cto %d rgsl %d rgsz %d inw %d inr %d sz %d hip %d en %d",
92            (uint) ctrl,
93            ctrl & DMA_CH0_CTRL_TRIG_AHB_ERROR_BITS ? 1 : 0,
94            ctrl & DMA_CH0_CTRL_TRIG_READ_ERROR_BITS ? 1 : 0,
95            ctrl & DMA_CH0_CTRL_TRIG_WRITE_ERROR_BITS ? 1 : 0,
96            ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS ? 1 : 0,
97            (int) ((ctrl & DMA_CH0_CTRL_TRIG_TREQ_SEL_BITS) >> DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB),
98            (int) ((ctrl & DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) >> DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB),
99            ctrl & DMA_CH0_CTRL_TRIG_RING_SEL_BITS ? 1 : 0,
100            rgsz ? (1 << rgsz) : 0,
101            ctrl & DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS ? 1 : 0,
102            ctrl & DMA_CH0_CTRL_TRIG_INCR_READ_BITS ? 1 : 0,
103            1 << ((ctrl & DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) >> DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB),
104            ctrl & DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS ? 1 : 0,
105            ctrl & DMA_CH0_CTRL_TRIG_EN_BITS ? 1 : 0);
106 }
107 #endif
108 
109 #if PARAM_ASSERTIONS_ENABLED(HARDWARE_DMA)
check_dma_channel_param_impl(uint __unused channel)110 void check_dma_channel_param_impl(uint __unused channel) {
111     valid_params_if(HARDWARE_DMA, channel < NUM_DMA_CHANNELS);
112 }
113 #endif
114