xref: /aosp_15_r20/external/pigweed/pw_dma_mcuxpresso/public/pw_dma_mcuxpresso/dma.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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