1 /*
2 * Copyright (c) 2023 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_dmav2_drv.h"
9
dma_default_channel_config(DMAV2_Type * ptr,dma_channel_config_t * ch)10 void dma_default_channel_config(DMAV2_Type *ptr, dma_channel_config_t *ch)
11 {
12 (void) ptr;
13 ch->en_infiniteloop = false;
14 ch->handshake_opt = DMA_HANDSHAKE_OPT_ONE_BURST;
15 ch->burst_opt = DMA_SRC_BURST_OPT_STANDAND_SIZE;
16 ch->priority = DMA_CHANNEL_PRIORITY_LOW;
17 ch->src_mode = DMA_HANDSHAKE_MODE_NORMAL;
18 ch->dst_mode = DMA_HANDSHAKE_MODE_NORMAL;
19 ch->src_burst_size = DMA_NUM_TRANSFER_PER_BURST_1T;
20 ch->src_addr_ctrl = DMA_ADDRESS_CONTROL_INCREMENT;
21 ch->dst_addr_ctrl = DMA_ADDRESS_CONTROL_INCREMENT;
22 ch->interrupt_mask = DMA_INTERRUPT_MASK_HALF_TC; /* disable half complete interrupt to keep align with dma */
23 ch->linked_ptr = 0;
24 #if defined(HPM_IP_FEATURE_DMAV2_BURST_IN_FIXED_TRANS) && (HPM_IP_FEATURE_DMAV2_BURST_IN_FIXED_TRANS == 1)
25 ch->en_src_burst_in_fixed_trans = false;
26 ch->en_dst_burst_in_fixed_trans = false;
27 #endif
28 #if defined(HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP) && (HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP == 1)
29 ch->swap_mode = DMA_SWAP_MODE_TABLE;
30 ch->swap_table = 0;
31 #endif
32 }
33
dma_setup_channel(DMAV2_Type * ptr,uint8_t ch_num,dma_channel_config_t * ch,bool start_transfer)34 hpm_stat_t dma_setup_channel(DMAV2_Type *ptr, uint8_t ch_num, dma_channel_config_t *ch, bool start_transfer)
35 {
36 uint32_t tmp;
37
38 if ((ch->dst_width > DMA_SOC_TRANSFER_WIDTH_MAX(ptr))
39 || (ch->src_width > DMA_SOC_TRANSFER_WIDTH_MAX(ptr))
40 || (ch_num >= DMA_SOC_CHANNEL_NUM)
41 || (ch->en_infiniteloop && (ch->linked_ptr != 0))
42 || ((ch->dst_mode == DMA_HANDSHAKE_MODE_HANDSHAKE) && (ch->src_mode == DMA_HANDSHAKE_MODE_HANDSHAKE))) {
43 return status_invalid_argument;
44 }
45
46 if ((ch->size_in_byte & ((1 << ch->dst_width) - 1))
47 || (ch->src_addr & ((1 << ch->src_width) - 1))
48 || (ch->dst_addr & ((1 << ch->dst_width) - 1))
49 || ((1 << ch->src_width) & ((1 << ch->dst_width) - 1))
50 || ((ch->linked_ptr & 0x7))) {
51 return status_dma_alignment_error;
52 }
53
54 ptr->CHCTRL[ch_num].SRCADDR = DMAV2_CHCTRL_SRCADDR_SRCADDRL_SET(ch->src_addr);
55 ptr->CHCTRL[ch_num].DSTADDR = DMAV2_CHCTRL_DSTADDR_DSTADDRL_SET(ch->dst_addr);
56 ptr->CHCTRL[ch_num].TRANSIZE = DMAV2_CHCTRL_TRANSIZE_TRANSIZE_SET(ch->size_in_byte >> ch->src_width);
57 ptr->CHCTRL[ch_num].LLPOINTER = DMAV2_CHCTRL_LLPOINTER_LLPOINTERL_SET(ch->linked_ptr >> DMAV2_CHCTRL_LLPOINTER_LLPOINTERL_SHIFT);
58 ptr->CHCTRL[ch_num].CHANREQCTRL = DMAV2_CHCTRL_CHANREQCTRL_SRCREQSEL_SET(ch_num) | DMAV2_CHCTRL_CHANREQCTRL_DSTREQSEL_SET(ch_num);
59 #if defined(HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP) && (HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP == 1)
60 ptr->CHCTRL[ch_num].SWAPTABLE = ch->swap_table;
61 #endif
62
63 dma_clear_transfer_status(ptr, ch_num);
64 tmp = DMAV2_CHCTRL_CTRL_INFINITELOOP_SET(ch->en_infiniteloop)
65 | DMAV2_CHCTRL_CTRL_HANDSHAKEOPT_SET(ch->handshake_opt)
66 | DMAV2_CHCTRL_CTRL_BURSTOPT_SET(ch->burst_opt)
67 | DMAV2_CHCTRL_CTRL_PRIORITY_SET(ch->priority)
68 | DMAV2_CHCTRL_CTRL_SRCBURSTSIZE_SET(ch->src_burst_size)
69 | DMAV2_CHCTRL_CTRL_SRCWIDTH_SET(ch->src_width)
70 | DMAV2_CHCTRL_CTRL_DSTWIDTH_SET(ch->dst_width)
71 | DMAV2_CHCTRL_CTRL_SRCMODE_SET(ch->src_mode)
72 | DMAV2_CHCTRL_CTRL_DSTMODE_SET(ch->dst_mode)
73 | DMAV2_CHCTRL_CTRL_SRCADDRCTRL_SET(ch->src_addr_ctrl)
74 | DMAV2_CHCTRL_CTRL_DSTADDRCTRL_SET(ch->dst_addr_ctrl)
75 #if defined(HPM_IP_FEATURE_DMAV2_BURST_IN_FIXED_TRANS) && (HPM_IP_FEATURE_DMAV2_BURST_IN_FIXED_TRANS == 1)
76 | DMAV2_CHCTRL_CTRL_SRC_FIXBURST_SET(ch->en_src_burst_in_fixed_trans)
77 | DMAV2_CHCTRL_CTRL_DST_FIXBURST_SET(ch->en_dst_burst_in_fixed_trans)
78 #endif
79 #if defined(HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP) && (HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP == 1)
80 | DMAV2_CHCTRL_CTRL_SWAP_CTL_SET(ch->swap_mode)
81 #endif
82 | ch->interrupt_mask;
83
84 if (start_transfer) {
85 tmp |= DMAV2_CHCTRL_CTRL_ENABLE_MASK;
86 }
87 ptr->CHCTRL[ch_num].CTRL = tmp;
88
89 return status_success;
90 }
91
dma_config_linked_descriptor(DMAV2_Type * ptr,dma_linked_descriptor_t * descriptor,uint8_t ch_num,dma_channel_config_t * config)92 hpm_stat_t dma_config_linked_descriptor(DMAV2_Type *ptr, dma_linked_descriptor_t *descriptor, uint8_t ch_num, dma_channel_config_t *config)
93 {
94 (void) ptr;
95 uint32_t tmp;
96
97 if ((config->dst_width > DMA_SOC_TRANSFER_WIDTH_MAX(ptr))
98 || (config->src_width > DMA_SOC_TRANSFER_WIDTH_MAX(ptr))
99 || (ch_num >= DMA_SOC_CHANNEL_NUM)
100 || (config->en_infiniteloop)
101 || ((config->dst_mode == DMA_HANDSHAKE_MODE_HANDSHAKE) && (config->src_mode == DMA_HANDSHAKE_MODE_HANDSHAKE))) {
102 return status_invalid_argument;
103 }
104
105 if ((config->size_in_byte & ((1 << config->dst_width) - 1))
106 || (config->src_addr & ((1 << config->src_width) - 1))
107 || (config->dst_addr & ((1 << config->dst_width) - 1))
108 || ((1 << config->src_width) & ((1 << config->dst_width) - 1))
109 || ((config->linked_ptr & 0x7))) {
110 return status_dma_alignment_error;
111 }
112
113 descriptor->src_addr = DMAV2_CHCTRL_SRCADDR_SRCADDRL_SET(config->src_addr);
114 descriptor->dst_addr = DMAV2_CHCTRL_DSTADDR_DSTADDRL_SET(config->dst_addr);
115 descriptor->trans_size = DMAV2_CHCTRL_TRANSIZE_TRANSIZE_SET(config->size_in_byte >> config->src_width);
116 descriptor->linked_ptr = DMAV2_CHCTRL_LLPOINTER_LLPOINTERL_SET(config->linked_ptr >> DMAV2_CHCTRL_LLPOINTER_LLPOINTERL_SHIFT);
117 descriptor->req_ctrl = DMAV2_CHCTRL_CHANREQCTRL_SRCREQSEL_SET(ch_num) | DMAV2_CHCTRL_CHANREQCTRL_DSTREQSEL_SET(ch_num);
118 #if defined(HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP) && (HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP == 1)
119 descriptor->swap_table = config->swap_table;
120 #endif
121
122 tmp = DMAV2_CHCTRL_CTRL_INFINITELOOP_SET(false)
123 | DMAV2_CHCTRL_CTRL_HANDSHAKEOPT_SET(config->handshake_opt)
124 | DMAV2_CHCTRL_CTRL_BURSTOPT_SET(config->burst_opt)
125 | DMAV2_CHCTRL_CTRL_PRIORITY_SET(config->priority)
126 | DMAV2_CHCTRL_CTRL_SRCBURSTSIZE_SET(config->src_burst_size)
127 | DMAV2_CHCTRL_CTRL_SRCWIDTH_SET(config->src_width)
128 | DMAV2_CHCTRL_CTRL_DSTWIDTH_SET(config->dst_width)
129 | DMAV2_CHCTRL_CTRL_SRCMODE_SET(config->src_mode)
130 | DMAV2_CHCTRL_CTRL_DSTMODE_SET(config->dst_mode)
131 | DMAV2_CHCTRL_CTRL_SRCADDRCTRL_SET(config->src_addr_ctrl)
132 | DMAV2_CHCTRL_CTRL_DSTADDRCTRL_SET(config->dst_addr_ctrl)
133 #if defined(HPM_IP_FEATURE_DMAV2_BURST_IN_FIXED_TRANS) && (HPM_IP_FEATURE_DMAV2_BURST_IN_FIXED_TRANS == 1)
134 | DMAV2_CHCTRL_CTRL_SRC_FIXBURST_SET(config->en_src_burst_in_fixed_trans)
135 | DMAV2_CHCTRL_CTRL_DST_FIXBURST_SET(config->en_dst_burst_in_fixed_trans)
136 #endif
137 #if defined(HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP) && (HPM_IP_FEATURE_DMAV2_BYTE_ORDER_SWAP == 1)
138 | DMAV2_CHCTRL_CTRL_SWAP_CTL_SET(config->swap_mode)
139 #endif
140 | config->interrupt_mask
141 | DMAV2_CHCTRL_CTRL_ENABLE_MASK;
142 descriptor->ctrl = tmp;
143
144 return status_success;
145 }
146
dma_start_memcpy(DMAV2_Type * ptr,uint8_t ch_num,uint32_t dst,uint32_t src,uint32_t size,uint32_t burst_len_in_byte)147 hpm_stat_t dma_start_memcpy(DMAV2_Type *ptr, uint8_t ch_num,
148 uint32_t dst, uint32_t src,
149 uint32_t size, uint32_t burst_len_in_byte)
150 {
151 hpm_stat_t stat = status_success;
152 uint32_t width, count;
153 uint32_t burst_size;
154 dma_channel_config_t config = {0};
155 dma_default_channel_config(ptr, &config);
156
157 /* burst size checking (1-byte burst length will cause heavy overhead */
158 if (!burst_len_in_byte || burst_len_in_byte == 1 || burst_len_in_byte > size
159 || burst_len_in_byte >
160 (uint32_t) ((1 << DMA_SOC_TRANSFER_WIDTH_MAX(ptr)) << DMA_SOC_TRANSFER_PER_BURST_MAX(ptr))) {
161 return status_invalid_argument;
162 }
163
164 count = count_set_bits(burst_len_in_byte);
165 if ((count > 1) || (burst_len_in_byte & 0x1)) {
166 /* dma only supports 2^n bytes as burst size */
167 return status_invalid_argument;
168 }
169
170 if ((size & (burst_len_in_byte - 1))) {
171 return status_dma_alignment_error;
172 }
173 burst_size = get_first_set_bit_from_lsb(burst_len_in_byte);
174
175 config.src_width = DMA_TRANSFER_WIDTH_HALF_WORD;
176 config.dst_width = DMA_TRANSFER_WIDTH_HALF_WORD;
177 for (width = DMA_SOC_TRANSFER_WIDTH_MAX(ptr); width > DMA_TRANSFER_WIDTH_HALF_WORD; width--) {
178 if (!(burst_len_in_byte & ((1 << width) - 1))
179 && !(dst & ((1 << width) - 1))
180 && !(src & ((1 << width) - 1))
181 && !(size & ((1 << width) - 1))) {
182 config.src_width = width;
183 config.dst_width = width;
184 break;
185 }
186 }
187
188 burst_size -= config.src_width;
189 do {
190 if (!(src & (((1 << config.src_width) << burst_size) - 1))) {
191 break;
192 }
193 burst_size--;
194 } while (burst_size > 0);
195
196 config.src_addr = src;
197 config.dst_addr = dst;
198 config.size_in_byte = size;
199
200 config.src_burst_size = burst_size;
201 stat = dma_setup_channel(ptr, ch_num, &config, true);
202 if (stat != status_success) {
203 return stat;
204 }
205
206 return stat;
207 }
208
dma_default_handshake_config(DMAV2_Type * ptr,dma_handshake_config_t * config)209 void dma_default_handshake_config(DMAV2_Type *ptr, dma_handshake_config_t *config)
210 {
211 (void) ptr;
212 memset(config, 0, sizeof(dma_handshake_config_t));
213 config->en_infiniteloop = false;
214 config->interrupt_mask = DMA_INTERRUPT_MASK_HALF_TC;
215 }
216
dma_setup_handshake(DMAV2_Type * ptr,dma_handshake_config_t * pconfig,bool start_transfer)217 hpm_stat_t dma_setup_handshake(DMAV2_Type *ptr, dma_handshake_config_t *pconfig, bool start_transfer)
218 {
219 hpm_stat_t stat = status_success;
220 dma_channel_config_t config = {0};
221 dma_default_channel_config(ptr, &config);
222
223 if (true == pconfig->dst_fixed) {
224 config.dst_addr_ctrl = DMA_ADDRESS_CONTROL_FIXED;
225 config.dst_mode = DMA_HANDSHAKE_MODE_HANDSHAKE;
226 }
227 if (true == pconfig->src_fixed) {
228 config.src_addr_ctrl = DMA_ADDRESS_CONTROL_FIXED;
229 config.src_mode = DMA_HANDSHAKE_MODE_HANDSHAKE;
230 }
231
232 if (pconfig->ch_index >= DMA_SOC_CHANNEL_NUM) {
233 return status_invalid_argument;
234 }
235
236 config.en_infiniteloop = pconfig->en_infiniteloop;
237 config.interrupt_mask = pconfig->interrupt_mask;
238 config.src_width = pconfig->data_width;
239 config.dst_width = pconfig->data_width;
240 config.src_addr = pconfig->src;
241 config.dst_addr = pconfig->dst;
242 config.size_in_byte = pconfig->size_in_byte;
243 /* In DMA handshake case, source burst size must be 1 transfer, that is 0. */
244 config.src_burst_size = 0;
245 stat = dma_setup_channel(ptr, pconfig->ch_index, &config, start_transfer);
246 if (stat != status_success) {
247 return stat;
248 }
249 return stat;
250 }
251