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