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