1 
2 /**
3  * \file
4  *
5  * \brief SAM DMA Controller (XDMAC) Interface
6  *
7  * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries.
8  *
9  * \asf_license_start
10  *
11  * \page License
12  *
13  * Subject to your compliance with these terms, you may use Microchip
14  * software and any derivatives exclusively with Microchip products.
15  * It is your responsibility to comply with third party license terms applicable
16  * to your use of third party software (including open source software) that
17  * may accompany Microchip software.
18  *
19  * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
20  * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
21  * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
22  * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
23  * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
24  * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
25  * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
26  * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
27  * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
28  * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
29  * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
30  *
31  * \asf_license_stop
32  *
33  */
34 
35 #include <hpl_dma.h>
36 #include <hpl_xdmac_config.h>
37 #include <peripheral_clk_config.h>
38 #include <utils.h>
39 #include <utils_assert.h>
40 #include <utils_repeat_macro.h>
41 
42 #if CONF_DMA_ENABLE
43 #define DMAC_CH_NUM 24
44 
45 /* Array containing callbacks for DMAC channels */
46 static struct _dma_resource _resources[DMAC_CH_NUM];
47 
48 /* This macro DMAC configuration */
49 #define DMAC_CHANNEL_CFG(i, n)                                                                                         \
50 	{XDMAC_CC_MBSIZE(CONF_DMAC_BURSTSIZE_##n) | XDMAC_CC_CSIZE(CONF_DMAC_CHUNKSIZE_##n)                                \
51 	     | XDMAC_CC_DWIDTH(CONF_DMAC_BEATSIZE_##n) | (CONF_DMAC_SRC_INTERFACE_##n << XDMAC_CC_SIF_Pos)                 \
52 	     | (CONF_DMAC_DES_INTERFACE_##n << XDMAC_CC_DIF_Pos)                                                           \
53 	     | XDMAC_CC_SAM((CONF_DMAC_SRC_STRIDE_##n == -1) ? 3 : CONF_DMAC_SRCINC_##n)                                   \
54 	     | XDMAC_CC_DAM((CONF_DMAC_DES_STRIDE_##n == -1) ? 3 : CONF_DMAC_DSTINC_##n)                                   \
55 	     | (CONF_DMAC_TYPE_##n << XDMAC_CC_TYPE_Pos) | (CONF_DMAC_DSYNC_##n << XDMAC_CC_DSYNC_Pos)                     \
56 	     | (CONF_DMAC_SWREQ_##n << XDMAC_CC_SWREQ_Pos)                                                                 \
57 	     | ((!CONF_DMAC_SWREQ_##n) ? XDMAC_CC_PERID(CONF_DMAC_TRIGSRC_##n) : 0x0),                                     \
58 	 XDMAC_CDS_MSP_SDS_MSP(CONF_DMAC_SRC_STRIDE_##n) | XDMAC_CDS_MSP_DDS_MSP(CONF_DMAC_DES_STRIDE_##n)},
59 
60 /* DMAC channel configuration */
61 struct dmac_channel_cfg {
62 	uint32_t config_reg;
63 	uint32_t cds_msp;
64 };
65 
66 /* DMAC channel configurations */
67 const static struct dmac_channel_cfg _cfgs[] = {REPEAT_MACRO(DMAC_CHANNEL_CFG, i, DMAC_CH_NUM)};
68 
_dma_init(void)69 int32_t _dma_init(void)
70 {
71 	uint8_t i;
72 
73 	/* Clear the pending Interrupt Status bit */
74 	for (i = 0; i < DMAC_CH_NUM; i++) {
75 		hri_xdmac_read_CIS_reg(XDMAC, i);
76 	}
77 	/* Configuration Registers */
78 	for (i = 0; i < DMAC_CH_NUM; i++) {
79 		hri_xdmac_write_CNDC_reg(XDMAC, i, 0x0);
80 		hri_xdmac_write_CBC_reg(XDMAC, i, 0x0);
81 		hri_xdmac_write_CSUS_reg(XDMAC, i, 0x0);
82 		hri_xdmac_write_CDUS_reg(XDMAC, i, 0x0);
83 		hri_xdmac_write_CC_reg(XDMAC, i, _cfgs[i].config_reg);
84 		hri_xdmac_write_CDS_MSP_reg(XDMAC, i, _cfgs[i].cds_msp);
85 	}
86 
87 	NVIC_DisableIRQ(XDMAC_IRQn);
88 	NVIC_ClearPendingIRQ(XDMAC_IRQn);
89 	NVIC_EnableIRQ(XDMAC_IRQn);
90 
91 	return ERR_NONE;
92 }
93 
_dma_set_destination_address(const uint8_t channel,const void * const dst)94 int32_t _dma_set_destination_address(const uint8_t channel, const void *const dst)
95 {
96 	hri_xdmac_write_CDA_reg(XDMAC, channel, (uint32_t)dst);
97 
98 	return ERR_NONE;
99 }
100 
_dma_set_source_address(const uint8_t channel,const void * const src)101 int32_t _dma_set_source_address(const uint8_t channel, const void *const src)
102 {
103 	hri_xdmac_write_CSA_reg(XDMAC, channel, (uint32_t)src);
104 
105 	return ERR_NONE;
106 }
107 
_dma_srcinc_enable(const uint8_t channel,const bool enable)108 int32_t _dma_srcinc_enable(const uint8_t channel, const bool enable)
109 {
110 	if (!enable && (hri_xdmac_read_CC_DWIDTH_bf(XDMAC, channel) != XDMAC_CC_DWIDTH_WORD_Val)) {
111 		/* Errata: If XDMA is used to transfer 8-bit or 16-bit data in fixed source
112 		 * address or fixed destination address mode, source and destination addresses
113 		 * are incremented by 8-bit or 16-bit.
114 		 * Workaround: The user can fix the problem by setting the source addressing
115 		 * mode to use micro block and data striding with micro block stride set to 0 and
116 		 * data stride set to -1.
117 		 */
118 		hri_xdmac_write_CC_SAM_bf(XDMAC, channel, XDMAC_CC_SAM_UBS_DS_AM_Val);
119 		hri_xdmac_write_CDS_MSP_SDS_MSP_bf(XDMAC, channel, (int16_t)(-1));
120 	} else {
121 		hri_xdmac_write_CC_SAM_bf(XDMAC, channel, enable);
122 	}
123 
124 	return ERR_NONE;
125 }
126 
_dma_dstinc_enable(const uint8_t channel,const bool enable)127 int32_t _dma_dstinc_enable(const uint8_t channel, const bool enable)
128 {
129 	if (!enable && (hri_xdmac_read_CC_DWIDTH_bf(XDMAC, channel) != XDMAC_CC_DWIDTH_WORD_Val)) {
130 		/* Errata: If XDMA is used to transfer 8-bit or 16-bit data in fixed source
131 		 * address or fixed destination address mode, source and destination addresses
132 		 * are incremented by 8-bit or 16-bit.
133 		 * Workaround: The user can fix the problem by setting the source addressing
134 		 * mode to use micro block and data striding with micro block stride set to 0 and
135 		 * data stride set to -1.
136 		 */
137 		hri_xdmac_write_CC_DAM_bf(XDMAC, channel, XDMAC_CC_DAM_UBS_DS_AM_Val);
138 		hri_xdmac_write_CDS_MSP_SDS_MSP_bf(XDMAC, channel, (int16_t)(-1));
139 	} else {
140 		hri_xdmac_write_CC_DAM_bf(XDMAC, channel, enable);
141 	}
142 
143 	return ERR_NONE;
144 }
145 
_dma_set_data_amount(const uint8_t channel,const uint32_t amount)146 int32_t _dma_set_data_amount(const uint8_t channel, const uint32_t amount)
147 {
148 	uint8_t width;
149 
150 	width = hri_xdmac_get_CC_DWIDTH_bf(XDMAC, channel, 0x3);
151 	hri_xdmac_write_CUBC_reg(XDMAC, channel, amount >> width);
152 
153 	return ERR_NONE;
154 }
155 
_dma_enable_transaction(const uint8_t channel,const bool software_trigger)156 int32_t _dma_enable_transaction(const uint8_t channel, const bool software_trigger)
157 {
158 	hri_xdmac_set_GS_reg(XDMAC, 1 << channel);
159 
160 	if (software_trigger) {
161 		hri_xdmac_write_GSWR_reg(XDMAC, 1 << channel);
162 	}
163 
164 	return ERR_NONE;
165 }
166 
_dma_get_channel_resource(struct _dma_resource ** resource,const uint8_t channel)167 int32_t _dma_get_channel_resource(struct _dma_resource **resource, const uint8_t channel)
168 {
169 	*resource = &_resources[channel];
170 
171 	return ERR_NONE;
172 }
173 
_dma_set_irq_state(const uint8_t channel,const enum _dma_callback_type type,const bool state)174 void _dma_set_irq_state(const uint8_t channel, const enum _dma_callback_type type, const bool state)
175 {
176 	if (state) {
177 		if (type == DMA_TRANSFER_COMPLETE_CB) {
178 			hri_xdmac_set_CIM_reg(XDMAC, channel, XDMAC_CIE_BIE);
179 		} else if (type == DMA_TRANSFER_ERROR_CB) {
180 			hri_xdmac_set_CIM_reg(XDMAC, channel, XDMAC_CIE_RBIE | XDMAC_CIE_WBIE | XDMAC_CIE_ROIE);
181 		}
182 		hri_xdmac_set_GIM_reg(XDMAC, (1 << channel));
183 	} else {
184 		if (type == DMA_TRANSFER_COMPLETE_CB) {
185 			hri_xdmac_clear_CIM_reg(XDMAC, channel, XDMAC_CID_BID);
186 		} else if (type == DMA_TRANSFER_ERROR_CB) {
187 			hri_xdmac_clear_CIM_reg(XDMAC, channel, XDMAC_CID_RBEID | XDMAC_CID_WBEID | XDMAC_CID_ROID);
188 		}
189 		hri_xdmac_clear_GIM_reg(XDMAC, (1 << channel));
190 	}
191 }
192 
193 /**
194  * \internal XDMAC interrupt handler
195  */
XDMAC_Handler(void)196 void XDMAC_Handler(void)
197 {
198 	uint32_t              status;
199 	struct _dma_resource *tmp_resource;
200 	int8_t                channel = -1;
201 	uint32_t              pend;
202 
203 	pend = hri_xdmac_read_GIS_reg(XDMAC);
204 
205 	/* Get the first pending channel ID */
206 	for (uint8_t i = 0; i < DMAC_CH_NUM; i++) {
207 		if ((pend >> i) & 1) {
208 			channel = i;
209 			break;
210 		}
211 	}
212 
213 	if (channel < 0) {
214 		return;
215 	}
216 
217 	tmp_resource = &_resources[channel];
218 	status       = hri_xdmac_read_CIS_reg(XDMAC, channel);
219 
220 	if (status & (XDMAC_CIS_RBEIS | XDMAC_CIS_WBEIS | XDMAC_CIS_ROIS)) {
221 		tmp_resource->dma_cb.error(tmp_resource);
222 	} else if (status & XDMAC_CIS_BIS) {
223 		tmp_resource->dma_cb.transfer_done(tmp_resource);
224 	}
225 }
226 
227 #endif /* CONF_DMA_ENABLE */
228