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