1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include <cstdint>
17
18 #include "fsl_dma.h"
19 #include "pw_assert/check.h"
20 #include "pw_status/status.h"
21
22 namespace pw::dma {
23
24 class McuxpressoDmaController;
25
26 // Represents a single channel of a DMA controller.
27 //
28 // NOTE: Because the SDK maintains a permanent reference to this class's
29 // members, these objects must have static lifetime at the time Init() is
30 // called and ever after. The destructor will intentionally crash.
31 class McuxpressoDmaChannel {
32 friend McuxpressoDmaController;
33
34 public:
35 McuxpressoDmaChannel(const McuxpressoDmaChannel&) = delete;
36 McuxpressoDmaChannel& operator=(const McuxpressoDmaChannel&) = delete;
37
~McuxpressoDmaChannel()38 ~McuxpressoDmaChannel() {
39 if (initialized_) {
40 PW_CRASH("Destruction of initialized McuxpressoDmaChannel not supported");
41 }
42 }
43
44 // NOTE: No locks are required for per-channel operations (since b/326468328).
45
Init()46 void Init() {
47 if (initialized_) {
48 return;
49 }
50
51 // NOTE: DMA_CreateHandle() registers the handle in a global array
52 // (s_DMAHandle) which is referenced by the DMA IRQ handler, and there
53 // unfortunately no way to unregister it, so this object must have static
54 // lifetime. The destructor will call PW_CRASH to try and enforce that.
55 DMA_CreateHandle(&handle_, controller_base(), channel_);
56
57 // Note: This automatically enables channel interrupts.
58 initialized_ = true;
59 }
60
Enable()61 void Enable() { DMA_EnableChannel(controller_base(), channel_); }
62
Disable()63 void Disable() { DMA_DisableChannel(controller_base(), channel_); }
64
SetPriority(uint32_t priority)65 void SetPriority(uint32_t priority) {
66 // TODO(jrreinhart) Make priority a class to check at compile-time
67 // and/or return Status and check at runtime. Valid values are:
68 // kDMA_ChannelPriority0 (0) (highest) to kDMA_ChannelPriority7 (7)
69 // (lowest).
70 DMA_SetChannelPriority(
71 controller_base(), channel_, static_cast<dma_priority_t>(priority));
72 }
73
74 // "A DMA channel is considered active when a DMA operation has been started
75 // but not yet fully completed."
IsActive()76 bool IsActive() { return DMA_ChannelIsActive(controller_base(), channel_); }
77
78 // "A DMA channel is considered busy when there is any operation related to
79 // that channel in the DMA controller’s internal pipeline. This information
80 // can be used after a DMA channel is disabled by software (but still
81 // active), allowing confirmation that there are no remaining operations in
82 // progress for that channel."
IsBusy()83 bool IsBusy() { return DMA_ChannelIsBusy(controller_base(), channel_); }
84
EnableInterrupts()85 void EnableInterrupts() {
86 DMA_EnableChannelInterrupts(controller_base(), channel_);
87 }
88
DisableInterrupts()89 void DisableInterrupts() {
90 DMA_DisableChannelInterrupts(controller_base(), channel_);
91 }
92
handle()93 dma_handle_t* handle() { return &handle_; }
94
95 private:
McuxpressoDmaChannel(McuxpressoDmaController & controller,uint32_t channel)96 explicit McuxpressoDmaChannel(McuxpressoDmaController& controller,
97 uint32_t channel)
98 : controller_(controller), channel_(channel) {}
99
100 DMA_Type* controller_base() const;
101
102 McuxpressoDmaController& controller_;
103 uint32_t const channel_;
104 dma_handle_t handle_;
105 bool initialized_ = false;
106 };
107
108 // Represents a DMA Controller.
109 class McuxpressoDmaController {
110 public:
McuxpressoDmaController(uintptr_t base_address)111 constexpr McuxpressoDmaController(uintptr_t base_address)
112 : base_address_(base_address) {}
113
114 McuxpressoDmaController(const McuxpressoDmaController&) = delete;
115 McuxpressoDmaController& operator=(const McuxpressoDmaController&) = delete;
116
Init()117 Status Init() {
118 DMA_Init(base());
119 return OkStatus();
120 }
121
122 // Get a channel object for the given channel number.
123 //
124 // NOTE: You must call Init() on the resulting object.
125 //
126 // NOTE: The resulting object *must* have static lifetime when Init() is
127 // called, and ever after.
GetChannel(uint32_t channel)128 McuxpressoDmaChannel GetChannel(uint32_t channel) {
129 return McuxpressoDmaChannel(*this, channel);
130 }
base()131 DMA_Type* base() const { return reinterpret_cast<DMA_Type*>(base_address_); }
132
133 private:
134 uintptr_t const base_address_;
135 };
136
controller_base()137 inline DMA_Type* McuxpressoDmaChannel::controller_base() const {
138 return controller_.base();
139 }
140
141 } // namespace pw::dma
142