• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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