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