1 /*
2 * Copyright (c) 2021-2022 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_dma_drv.h"
9
dma_setup_channel(DMA_Type * ptr,uint8_t ch_num,dma_channel_config_t * ch,bool start_transfer)10 hpm_stat_t dma_setup_channel(DMA_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->dst_mode == DMA_HANDSHAKE_MODE_HANDSHAKE) && (ch->src_mode == DMA_HANDSHAKE_MODE_HANDSHAKE))) {
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 = DMA_CHCTRL_SRCADDR_SRCADDRL_SET(ch->src_addr);
28 ptr->CHCTRL[ch_num].DSTADDR = DMA_CHCTRL_DSTADDR_DSTADDRL_SET(ch->dst_addr);
29 ptr->CHCTRL[ch_num].TRANSIZE = DMA_CHCTRL_TRANSIZE_TRANSIZE_SET(ch->size_in_byte >> ch->src_width);
30 ptr->CHCTRL[ch_num].LLPOINTER = DMA_CHCTRL_LLPOINTER_LLPOINTERL_SET(ch->linked_ptr >> DMA_CHCTRL_LLPOINTER_LLPOINTERL_SHIFT);
31
32 #if DMA_SUPPORT_64BIT_ADDR
33 ptr->CHCTRL[ch_num].SRCADDRH = DMA_CHCTRL_SRCADDRH_SRCADDRH_SET(ch->src_addr_high);
34 ptr->CHCTRL[ch_num].DSTADDRH = DMA_CHCTRL_DSTADDRH_DSTADDRH_SET(ch->dst_addr_high);
35 ptr->CHCTRL[ch_num].LLPOINTERH = DMA_CHCTRL_LLPOINTERH_LLPOINTERH_SET(ch->linked_ptr_high);
36 #endif
37
38 ptr->INTSTATUS = (DMA_INTSTATUS_TC_SET(1) | DMA_INTSTATUS_ABORT_SET(1) | DMA_INTSTATUS_ERROR_SET(1)) << ch_num;
39 tmp = DMA_CHCTRL_CTRL_SRCBUSINFIDX_SET(0)
40 | DMA_CHCTRL_CTRL_DSTBUSINFIDX_SET(0)
41 | DMA_CHCTRL_CTRL_PRIORITY_SET(ch->priority)
42 | DMA_CHCTRL_CTRL_SRCBURSTSIZE_SET(ch->src_burst_size)
43 | DMA_CHCTRL_CTRL_SRCWIDTH_SET(ch->src_width)
44 | DMA_CHCTRL_CTRL_DSTWIDTH_SET(ch->dst_width)
45 | DMA_CHCTRL_CTRL_SRCMODE_SET(ch->src_mode)
46 | DMA_CHCTRL_CTRL_DSTMODE_SET(ch->dst_mode)
47 | DMA_CHCTRL_CTRL_SRCADDRCTRL_SET(ch->src_addr_ctrl)
48 | DMA_CHCTRL_CTRL_DSTADDRCTRL_SET(ch->dst_addr_ctrl)
49 | DMA_CHCTRL_CTRL_SRCREQSEL_SET(ch_num)
50 | DMA_CHCTRL_CTRL_DSTREQSEL_SET(ch_num)
51 | ch->interrupt_mask;
52
53 if (start_transfer) {
54 tmp |= DMA_CHCTRL_CTRL_ENABLE_MASK;
55 }
56 ptr->CHCTRL[ch_num].CTRL = tmp;
57
58 return status_success;
59 }
60
61
dma_default_channel_config(DMA_Type * ptr,dma_channel_config_t * ch)62 void dma_default_channel_config(DMA_Type *ptr, dma_channel_config_t *ch)
63 {
64 (void) ptr;
65 ch->priority = DMA_CHANNEL_PRIORITY_LOW;
66 ch->src_mode = DMA_HANDSHAKE_MODE_NORMAL;
67 ch->dst_mode = DMA_HANDSHAKE_MODE_NORMAL;
68 ch->src_burst_size = DMA_NUM_TRANSFER_PER_BURST_1T;
69 ch->src_addr_ctrl = DMA_ADDRESS_CONTROL_INCREMENT;
70 ch->dst_addr_ctrl = DMA_ADDRESS_CONTROL_INCREMENT;
71 ch->interrupt_mask = DMA_INTERRUPT_MASK_NONE;
72 ch->linked_ptr = 0;
73 #if DMA_SUPPORT_64BIT_ADDR
74 ch->linked_ptr_high = 0;
75 #endif
76 }
77
dma_config_linked_descriptor(DMA_Type * ptr,dma_linked_descriptor_t * descriptor,uint8_t ch_num,dma_channel_config_t * config)78 hpm_stat_t dma_config_linked_descriptor(DMA_Type *ptr, dma_linked_descriptor_t *descriptor, uint8_t ch_num, dma_channel_config_t *config)
79 {
80 uint32_t tmp;
81
82 if ((config->dst_width > DMA_SOC_TRANSFER_WIDTH_MAX(ptr))
83 || (config->src_width > DMA_SOC_TRANSFER_WIDTH_MAX(ptr))
84 || (ch_num >= DMA_SOC_CHANNEL_NUM)
85 || ((config->dst_mode == DMA_HANDSHAKE_MODE_HANDSHAKE) && (config->src_mode == DMA_HANDSHAKE_MODE_HANDSHAKE))) {
86 return status_invalid_argument;
87 }
88
89 if ((config->size_in_byte & ((1 << config->dst_width) - 1))
90 || (config->src_addr & ((1 << config->src_width) - 1))
91 || (config->dst_addr & ((1 << config->dst_width) - 1))
92 || ((1 << config->src_width) & ((1 << config->dst_width) - 1))
93 || ((config->linked_ptr & 0x7))) {
94 return status_dma_alignment_error;
95 }
96
97 descriptor->src_addr = DMA_CHCTRL_SRCADDR_SRCADDRL_SET(config->src_addr);
98 descriptor->dst_addr = DMA_CHCTRL_DSTADDR_DSTADDRL_SET(config->dst_addr);
99 descriptor->trans_size = DMA_CHCTRL_TRANSIZE_TRANSIZE_SET(config->size_in_byte >> config->src_width);
100 descriptor->linked_ptr = DMA_CHCTRL_LLPOINTER_LLPOINTERL_SET(config->linked_ptr >> DMA_CHCTRL_LLPOINTER_LLPOINTERL_SHIFT);
101
102 #if DMA_SUPPORT_64BIT_ADDR
103 descriptor->src_addr_high = DMA_CHCTRL_SRCADDRH_SRCADDRH_SET(config->src_addr_high);
104 descriptor->dst_addr_high = DMA_CHCTRL_DSTADDRH_DSTADDRH_SET(config->dst_addr_high);
105 descriptor->linked_ptr_high = DMA_CHCTRL_LLPOINTERH_LLPOINTERH_SET(config->linked_ptr_high);
106 #endif
107
108 tmp = DMA_CHCTRL_CTRL_SRCBUSINFIDX_SET(0)
109 | DMA_CHCTRL_CTRL_DSTBUSINFIDX_SET(0)
110 | DMA_CHCTRL_CTRL_PRIORITY_SET(config->priority)
111 | DMA_CHCTRL_CTRL_SRCBURSTSIZE_SET(config->src_burst_size)
112 | DMA_CHCTRL_CTRL_SRCWIDTH_SET(config->src_width)
113 | DMA_CHCTRL_CTRL_DSTWIDTH_SET(config->dst_width)
114 | DMA_CHCTRL_CTRL_SRCMODE_SET(config->src_mode)
115 | DMA_CHCTRL_CTRL_DSTMODE_SET(config->dst_mode)
116 | DMA_CHCTRL_CTRL_SRCADDRCTRL_SET(config->src_addr_ctrl)
117 | DMA_CHCTRL_CTRL_DSTADDRCTRL_SET(config->dst_addr_ctrl)
118 | DMA_CHCTRL_CTRL_SRCREQSEL_SET(ch_num)
119 | DMA_CHCTRL_CTRL_DSTREQSEL_SET(ch_num)
120 | config->interrupt_mask
121 | DMA_CHCTRL_CTRL_ENABLE_MASK;
122 descriptor->ctrl = tmp;
123
124 return status_success;
125 }
126
dma_start_memcpy(DMA_Type * ptr,uint8_t ch_num,uint32_t dst,uint32_t src,uint32_t size,uint32_t burst_len_in_byte)127 hpm_stat_t dma_start_memcpy(DMA_Type *ptr, uint8_t ch_num,
128 uint32_t dst, uint32_t src,
129 uint32_t size, uint32_t burst_len_in_byte)
130 {
131 hpm_stat_t stat = status_success;
132 uint32_t width, count;
133 int32_t burst_size;
134 dma_channel_config_t config = {0};
135 dma_default_channel_config(ptr, &config);
136
137 /* burst size checking (1-byte burst length will cause heavy overhead */
138 if (!burst_len_in_byte || burst_len_in_byte == 1 || burst_len_in_byte > size
139 || burst_len_in_byte >
140 (uint32_t) ((1 << DMA_SOC_TRANSFER_WIDTH_MAX(ptr)) << DMA_SOC_TRANSFER_PER_BURST_MAX(ptr))) {
141 return status_invalid_argument;
142 }
143
144 count = count_set_bits(burst_len_in_byte);
145 if ((count > 1) || (burst_len_in_byte & 0x1)) {
146 /* dma only supports 2^n bytes as burst size */
147 return status_invalid_argument;
148 }
149
150 if ((size & (burst_len_in_byte - 1))) {
151 return status_dma_alignment_error;
152 }
153 burst_size = get_first_set_bit_from_lsb(burst_len_in_byte);
154
155 config.src_width = DMA_TRANSFER_WIDTH_HALF_WORD;
156 config.dst_width = DMA_TRANSFER_WIDTH_HALF_WORD;
157 for (width = DMA_SOC_TRANSFER_WIDTH_MAX(ptr); width > DMA_TRANSFER_WIDTH_HALF_WORD; width--) {
158 if (!(burst_len_in_byte & ((1 << width) - 1))
159 && !(dst & ((1 << width) - 1))
160 && !(src & ((1 << width) - 1))
161 && !(size & ((1 << width) - 1))) {
162 config.src_width = width;
163 config.dst_width = width;
164 break;
165 }
166 }
167
168 burst_size -= config.src_width;
169 do {
170 if (!(src & (((1 << config.src_width) << burst_size) - 1))) {
171 break;
172 }
173 burst_size--;
174 } while (burst_size > 0);
175
176 config.src_addr = src;
177 config.dst_addr = dst;
178 config.size_in_byte = size;
179
180 config.src_burst_size = burst_size;
181 stat = dma_setup_channel(ptr, ch_num, &config, true);
182 if (stat != status_success) {
183 return stat;
184 }
185
186 return stat;
187 }
188
dma_default_handshake_config(DMA_Type * ptr,dma_handshake_config_t * config)189 void dma_default_handshake_config(DMA_Type *ptr, dma_handshake_config_t *config)
190 {
191 (void) ptr;
192 memset(config, 0, sizeof(dma_handshake_config_t));
193 }
194
dma_setup_handshake(DMA_Type * ptr,dma_handshake_config_t * pconfig,bool start_transfer)195 hpm_stat_t dma_setup_handshake(DMA_Type *ptr, dma_handshake_config_t *pconfig, bool start_transfer)
196 {
197 hpm_stat_t stat = status_success;
198 dma_channel_config_t config = {0};
199 dma_default_channel_config(ptr, &config);
200
201 if (true == pconfig->dst_fixed) {
202 config.dst_addr_ctrl = DMA_ADDRESS_CONTROL_FIXED;
203 config.dst_mode = DMA_HANDSHAKE_MODE_HANDSHAKE;
204 }
205 if (true == pconfig->src_fixed) {
206 config.src_addr_ctrl = DMA_ADDRESS_CONTROL_FIXED;
207 config.src_mode = DMA_HANDSHAKE_MODE_HANDSHAKE;
208 }
209
210 if (pconfig->ch_index >= DMA_SOC_CHANNEL_NUM) {
211 return status_invalid_argument;
212 }
213
214 config.src_width = pconfig->data_width;
215 config.dst_width = pconfig->data_width;
216 config.src_addr = pconfig->src;
217 config.dst_addr = pconfig->dst;
218 config.size_in_byte = pconfig->size_in_byte;
219 /* In DMA handshake case, source burst size must be 1 transfer, that is 0. */
220 config.src_burst_size = 0;
221 stat = dma_setup_channel(ptr, pconfig->ch_index, &config, start_transfer);
222 if (stat != status_success) {
223 return stat;
224 }
225 return stat;
226 }
227