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