1 /*
2 * Copyright 2023 NXP
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdint.h>
8 #include <stdbool.h>
9
10 #include "fsl_edma_rev2.h"
11
12 #define EDMA_READ(base, access)\
13 ((access) == kEDMA_RegAccess16 ?\
14 (uint32_t)EDMA_Read16(base) :\
15 EDMA_Read32(base))
16
17 #define EDMA_WRITE(base, value, access)\
18 ((access) == kEDMA_RegAccess16 ?\
19 EDMA_Write16(base, (uint16_t)value) :\
20 EDMA_Write32(base, value))
21
22 #define EDMA_REG_ACCESS(reg)\
23 (((reg) & EDMA_REGISTER_ACCESS_MASK) >> EDMA_REGISTER_ACCESS_SHIFT)
24
25 #define EDMA_REG_INDEX(reg) ((reg) & EDMA_REGISTER_INDEX_MASK)
26
EDMA_Write32(uint32_t addr,uint32_t value)27 static void EDMA_Write32(uint32_t addr, uint32_t value)
28 {
29 *(volatile uint32_t *)(uintptr_t)addr = value;
30 }
31
EDMA_Read32(uint32_t addr)32 static uint32_t EDMA_Read32(uint32_t addr)
33 {
34 return *(volatile uint32_t *)(uintptr_t)addr;
35 }
36
EDMA_Write16(uint32_t addr,uint16_t value)37 static void EDMA_Write16(uint32_t addr, uint16_t value)
38 {
39 *(volatile uint16_t *)(uintptr_t)addr = value;
40 }
41
EDMA_Read16(uint32_t addr)42 static uint16_t EDMA_Read16(uint32_t addr)
43 {
44 return *(volatile uint16_t *)(uintptr_t)addr;
45 }
46
EDMA_GetChannelBase(edma_config_t * cfg,int channel)47 static uint32_t EDMA_GetChannelBase(edma_config_t *cfg, int channel)
48 {
49 return cfg->regmap + cfg->channelOffset + channel * cfg->channelWidth;
50 }
51
EDMA_GetChannelRegBase(edma_config_t * cfg,int channel,uint32_t reg)52 static uint32_t EDMA_GetChannelRegBase(edma_config_t *cfg, int channel, uint32_t reg)
53 {
54 uint32_t index = EDMA_REG_INDEX(reg);
55
56 if (reg == EDMA_MP_CH_GRPRI || reg == EDMA_MP_CH_MUX) {
57 return cfg->regmap + cfg->registerLayout[index] + channel * 0x4;
58 } else {
59 return EDMA_GetChannelBase(cfg, channel) + cfg->registerLayout[index];
60 }
61 }
62
EDMA_TransferTypeIsValid(uint32_t transferType)63 static bool EDMA_TransferTypeIsValid(uint32_t transferType)
64 {
65 switch (transferType) {
66 case kEDMA_TransferTypeM2M:
67 case kEDMA_TransferTypeM2P:
68 case kEDMA_TransferTypeP2M:
69 return true;
70 default:
71 return false;
72 }
73
74 return false;
75 }
76
EDMA_ChannelRegWrite(edma_config_t * cfg,int channel,uint32_t reg,uint32_t value)77 void EDMA_ChannelRegWrite(edma_config_t *cfg, int channel, uint32_t reg, uint32_t value)
78 {
79 uint32_t base = EDMA_GetChannelRegBase(cfg, channel, reg);
80
81 EDMA_WRITE(base, value, EDMA_REG_ACCESS(reg));
82 }
83
EDMA_ChannelRegRead(edma_config_t * cfg,int channel,uint32_t reg)84 uint32_t EDMA_ChannelRegRead(edma_config_t *cfg, int channel, uint32_t reg)
85 {
86 uint32_t base = EDMA_GetChannelRegBase(cfg, channel, reg);
87
88 return EDMA_READ(base, EDMA_REG_ACCESS(reg));
89 }
90
EDMA_ChannelRegUpdate(edma_config_t * cfg,int channel,uint32_t reg,uint32_t set,uint32_t clear)91 void EDMA_ChannelRegUpdate(edma_config_t *cfg, int channel, uint32_t reg,
92 uint32_t set, uint32_t clear)
93 {
94 uint32_t base, val;
95
96 base = EDMA_GetChannelRegBase(cfg, channel, reg);
97 val = EDMA_READ(base, EDMA_REG_ACCESS(reg));
98
99 val &= ~clear;
100 val |= set;
101
102 EDMA_WRITE(base, val, EDMA_REG_ACCESS(reg));
103 }
104
EDMA_MPRegWrite(edma_config_t * cfg,uint32_t reg,uint32_t value)105 void EDMA_MPRegWrite(edma_config_t *cfg, uint32_t reg, uint32_t value)
106 {
107 uint32_t base = cfg->regmap + cfg->registerLayout[EDMA_REG_INDEX(reg)];
108
109 EDMA_WRITE(base, value, EDMA_REG_ACCESS(reg));
110 }
111
EDMA_MPRegRead(edma_config_t * cfg,uint32_t reg)112 uint32_t EDMA_MPRegRead(edma_config_t *cfg, uint32_t reg)
113 {
114 uint32_t base = cfg->regmap + cfg->registerLayout[EDMA_REG_INDEX(reg)];
115
116 return EDMA_READ(base, EDMA_REG_ACCESS(reg));
117 }
118
EDMA_SetChannelMux(edma_config_t * cfg,int channel,uint32_t mux)119 status_t EDMA_SetChannelMux(edma_config_t *cfg, int channel, uint32_t mux)
120 {
121 uint32_t muxReg;
122
123 if (channel >= cfg->channels) {
124 return kStatus_InvalidArgument;
125 }
126
127 if (!EDMA_HAS_MUX(cfg)) {
128 return kStatus_EDMA_InvalidConfiguration;
129 }
130
131 if (cfg->flags & EDMA_HAS_MP_MUX_FLAG) {
132 muxReg = EDMA_MP_CH_MUX;
133 } else {
134 muxReg = EDMA_TCD_CH_MUX;
135 }
136
137 if (EDMA_ChannelRegRead(cfg, channel, muxReg) != 0 && mux != 0) {
138 return kStatus_Busy;
139 }
140
141 EDMA_ChannelRegWrite(cfg, channel, muxReg, mux);
142
143 return kStatus_Success;
144 }
145
EDMA_ConvertTransferWidth(uint32_t width)146 static uint32_t EDMA_ConvertTransferWidth(uint32_t width)
147 {
148 switch (width) {
149 case kEDMA_TransferWidth1B:
150 return 0;
151 case kEDMA_TransferWidth2B:
152 return 1;
153 case kEDMA_TransferWidth4B:
154 return 2;
155 case kEDMA_TransferWidth8B:
156 return 3;
157 case kEDMA_TransferWidth16B:
158 return 4;
159 case kEDMA_TransferWidth32B:
160 return 5;
161 case kEDMA_TransferWidth64B:
162 return 6;
163 case kEDMA_TransferWidth128B:
164 return 7;
165 default:
166 return 0;
167 }
168
169 return 0;
170 }
171
EDMA_ConfigureTransfer(edma_config_t * cfg,int channel,uint32_t saddr,uint32_t daddr,uint32_t ssize,uint32_t dsize,uint32_t burstSize,uint32_t transferSize,uint32_t transferType)172 status_t EDMA_ConfigureTransfer(edma_config_t *cfg, int channel,
173 uint32_t saddr, uint32_t daddr,
174 uint32_t ssize, uint32_t dsize,
175 uint32_t burstSize, uint32_t transferSize,
176 uint32_t transferType)
177 {
178 uint32_t attr, biter, soff, doff;
179
180 /* check if configuration is valid */
181 if (!saddr || !daddr) {
182 return kStatus_InvalidArgument;
183 }
184
185 if (transferSize % burstSize) {
186 return kStatus_EDMA_InvalidConfiguration;
187 }
188
189 if (!EDMA_TransferWidthIsValid(cfg, ssize)) {
190 return kStatus_EDMA_InvalidConfiguration;
191 }
192
193 if (!EDMA_TransferWidthIsValid(cfg, dsize)) {
194 return kStatus_EDMA_InvalidConfiguration;
195 }
196
197 if (channel >= cfg->channels) {
198 return kStatus_InvalidArgument;
199 }
200
201 if (saddr % ssize) {
202 return kStatus_EDMA_InvalidConfiguration;
203 }
204
205 if (daddr % dsize) {
206 return kStatus_EDMA_InvalidConfiguration;
207 }
208
209 if (burstSize % ssize) {
210 return kStatus_EDMA_InvalidConfiguration;
211 }
212
213 if (burstSize % dsize) {
214 return kStatus_EDMA_InvalidConfiguration;
215 }
216
217 if (!EDMA_TransferTypeIsValid(transferType)) {
218 return kStatus_EDMA_InvalidConfiguration;
219 }
220
221 soff = ssize;
222 doff = dsize;
223
224 /* convert SSIZE and DSIZE to the format we can write to EDMA ATTR */
225 ssize = EDMA_ConvertTransferWidth(ssize);
226 dsize = EDMA_ConvertTransferWidth(dsize);
227
228 attr = EDMA_TCD_ATTR_SSIZE(ssize) | EDMA_TCD_ATTR_DSIZE(dsize);
229 biter = transferSize / burstSize;
230
231 switch (transferType) {
232 case kEDMA_TransferTypeM2P:
233 doff = 0;
234 break;
235 case kEDMA_TransferTypeP2M:
236 soff = 0;
237 break;
238 }
239
240 /* notes:
241 * 1) SOFF and DOFF are currently set to SSIZE and DSIZE.
242 * 2) channel linking is not currently supported.
243 */
244 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_SADDR, saddr);
245 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_DADDR, daddr);
246 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_ATTR, attr);
247 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_SOFF, soff);
248 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_DOFF, doff);
249 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_CITER, EDMA_TCD_CITER_ELINKNO(biter));
250 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_BITER, EDMA_TCD_BITER_ELINKNO(biter));
251 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_NBYTES, EDMA_TCD_NBYTES_MLOFFNO(burstSize));
252
253 if (cfg->flags & EDMA_HAS_64BIT_TCD_FLAG) {
254 /* EDMA version has 64-bit TCD but 64-bit addresses are not currently
255 * supported by the API. Pad higher 32 bits with 0s.
256 */
257 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_SADDR_HIGH, 0x0);
258 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_DADDR_HIGH, 0x0);
259 }
260
261 /* clean registers to make sure there's no leftover config */
262 EDMA_ChannelRegWrite(cfg, channel, EDMA_TCD_CSR, 0);
263
264 return kStatus_Success;
265 }
266