1 /*
2  * Copyright (c) 2018 Google LLC.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT atmel_sam0_dmac
8 
9 #include <zephyr/device.h>
10 #include <soc.h>
11 #include <zephyr/drivers/dma.h>
12 
13 #include <zephyr/logging/log.h>
14 #include <zephyr/irq.h>
15 LOG_MODULE_REGISTER(dma_sam0, CONFIG_DMA_LOG_LEVEL);
16 
17 #define DMA_REGS	((Dmac *)DT_INST_REG_ADDR(0))
18 
19 struct dma_sam0_channel {
20 	dma_callback_t cb;
21 	void *user_data;
22 };
23 
24 struct dma_sam0_data {
25 	__aligned(16) DmacDescriptor descriptors[DMAC_CH_NUM];
26 	__aligned(16) DmacDescriptor descriptors_wb[DMAC_CH_NUM];
27 	struct dma_sam0_channel channels[DMAC_CH_NUM];
28 };
29 
30 /* Handles DMA interrupts and dispatches to the individual channel */
dma_sam0_isr(const struct device * dev)31 static void dma_sam0_isr(const struct device *dev)
32 {
33 	struct dma_sam0_data *data = dev->data;
34 	struct dma_sam0_channel *chdata;
35 	uint16_t pend = DMA_REGS->INTPEND.reg;
36 	uint32_t channel;
37 
38 	/* Acknowledge all interrupts for the channel in pend */
39 	DMA_REGS->INTPEND.reg = pend;
40 
41 	channel = (pend & DMAC_INTPEND_ID_Msk) >> DMAC_INTPEND_ID_Pos;
42 	chdata = &data->channels[channel];
43 
44 	if (pend & DMAC_INTPEND_TERR) {
45 		if (chdata->cb) {
46 			chdata->cb(dev, chdata->user_data,
47 				   channel, -DMAC_INTPEND_TERR);
48 		}
49 	} else if (pend & DMAC_INTPEND_TCMPL) {
50 		if (chdata->cb) {
51 			chdata->cb(dev, chdata->user_data, channel, 0);
52 		}
53 	}
54 
55 	/*
56 	 * If more than one channel is pending, we'll just immediately
57 	 * interrupt again and handle it through a different INTPEND value.
58 	 */
59 }
60 
61 /* Configure a channel */
dma_sam0_config(const struct device * dev,uint32_t channel,struct dma_config * config)62 static int dma_sam0_config(const struct device *dev, uint32_t channel,
63 			   struct dma_config *config)
64 {
65 	struct dma_sam0_data *data = dev->data;
66 	DmacDescriptor *desc = &data->descriptors[channel];
67 	struct dma_block_config *block = config->head_block;
68 	struct dma_sam0_channel *channel_control;
69 	DMAC_BTCTRL_Type btctrl = { .reg = 0 };
70 	unsigned int key;
71 
72 	if (channel >= DMAC_CH_NUM) {
73 		LOG_ERR("Unsupported channel");
74 		return -EINVAL;
75 	}
76 
77 	if (config->block_count > 1) {
78 		LOG_ERR("Chained transfers not supported");
79 		/* TODO: add support for chained transfers. */
80 		return -ENOTSUP;
81 	}
82 
83 	if (config->dma_slot >= DMAC_TRIG_NUM) {
84 		LOG_ERR("Invalid trigger number");
85 		return -EINVAL;
86 	}
87 
88 	/* Lock and page in the channel configuration */
89 	key = irq_lock();
90 
91 	/*
92 	 * The "bigger" DMAC on some SAM0 chips (e.g. SAMD5x) has
93 	 * independently accessible registers for each channel, while
94 	 * the other ones require an indirect channel selection before
95 	 * accessing shared registers.  The simplest way to detect the
96 	 * difference is the presence of the DMAC_CHID_ID macro from the
97 	 * ASF HAL (i.e. it's only defined if indirect access is required).
98 	 */
99 #ifdef DMAC_CHID_ID
100 	/* Select the channel for configuration */
101 	DMA_REGS->CHID.reg = DMAC_CHID_ID(channel);
102 	DMA_REGS->CHCTRLA.reg = 0;
103 
104 	/* Connect the peripheral trigger */
105 	if (config->channel_direction == MEMORY_TO_MEMORY) {
106 		/*
107 		 * A single software trigger will start the
108 		 * transfer
109 		 */
110 		DMA_REGS->CHCTRLB.reg = DMAC_CHCTRLB_TRIGACT_TRANSACTION |
111 				    DMAC_CHCTRLB_TRIGSRC(config->dma_slot);
112 	} else {
113 		/* One peripheral trigger per beat */
114 		DMA_REGS->CHCTRLB.reg = DMAC_CHCTRLB_TRIGACT_BEAT |
115 				    DMAC_CHCTRLB_TRIGSRC(config->dma_slot);
116 	}
117 
118 	/* Set the priority */
119 	if (config->channel_priority >= DMAC_LVL_NUM) {
120 		LOG_ERR("Invalid priority");
121 		goto inval;
122 	}
123 
124 	DMA_REGS->CHCTRLB.bit.LVL = config->channel_priority;
125 
126 	/* Enable the interrupts */
127 	DMA_REGS->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL;
128 	if (!config->error_callback_dis) {
129 		DMA_REGS->CHINTENSET.reg = DMAC_CHINTENSET_TERR;
130 	} else {
131 		DMA_REGS->CHINTENCLR.reg = DMAC_CHINTENSET_TERR;
132 	}
133 
134 	DMA_REGS->CHINTFLAG.reg = DMAC_CHINTFLAG_TERR | DMAC_CHINTFLAG_TCMPL;
135 #else
136 	/* Channels have separate configuration registers */
137 	DmacChannel * chcfg = &DMA_REGS->Channel[channel];
138 
139 	if (config->channel_direction == MEMORY_TO_MEMORY) {
140 		/*
141 		 * A single software trigger will start the
142 		 * transfer
143 		 */
144 		chcfg->CHCTRLA.reg = DMAC_CHCTRLA_TRIGACT_TRANSACTION |
145 				     DMAC_CHCTRLA_TRIGSRC(config->dma_slot);
146 	} else if ((config->channel_direction == MEMORY_TO_PERIPHERAL) ||
147 		(config->channel_direction == PERIPHERAL_TO_MEMORY)) {
148 		/* One peripheral trigger per beat */
149 		chcfg->CHCTRLA.reg = DMAC_CHCTRLA_TRIGACT_BURST |
150 				     DMAC_CHCTRLA_TRIGSRC(config->dma_slot);
151 	} else {
152 		LOG_ERR("Direction error. %d", config->channel_direction);
153 		goto inval;
154 	}
155 
156 	/* Set the priority */
157 	if (config->channel_priority >= DMAC_LVL_NUM) {
158 		LOG_ERR("Invalid priority");
159 		goto inval;
160 	}
161 
162 	chcfg->CHPRILVL.bit.PRILVL = config->channel_priority;
163 
164 	/* Set the burst length */
165 	if (config->source_burst_length != config->dest_burst_length) {
166 		LOG_ERR("Source and destination burst lengths must be equal");
167 		goto inval;
168 	}
169 
170 	if (config->source_burst_length > 16U) {
171 		LOG_ERR("Invalid burst length");
172 		goto inval;
173 	}
174 
175 	if (config->source_burst_length > 0U) {
176 		chcfg->CHCTRLA.reg |= DMAC_CHCTRLA_BURSTLEN(
177 			config->source_burst_length - 1U);
178 	}
179 
180 	/* Enable the interrupts */
181 	chcfg->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL;
182 	if (!config->error_callback_dis) {
183 		chcfg->CHINTENSET.reg = DMAC_CHINTENSET_TERR;
184 	} else {
185 		chcfg->CHINTENCLR.reg = DMAC_CHINTENSET_TERR;
186 	}
187 
188 	chcfg->CHINTFLAG.reg = DMAC_CHINTFLAG_TERR | DMAC_CHINTFLAG_TCMPL;
189 #endif
190 
191 	/* Set the beat (single transfer) size */
192 	if (config->source_data_size != config->dest_data_size) {
193 		LOG_ERR("Source and destination data sizes must be equal");
194 		goto inval;
195 	}
196 
197 	switch (config->source_data_size) {
198 	case 1:
199 		btctrl.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val;
200 		break;
201 	case 2:
202 		btctrl.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_HWORD_Val;
203 		break;
204 	case 4:
205 		btctrl.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_WORD_Val;
206 		break;
207 	default:
208 		LOG_ERR("Invalid data size");
209 		goto inval;
210 	}
211 
212 	/* Set up the one and only block */
213 	desc->BTCNT.reg = block->block_size / config->source_data_size;
214 	desc->DESCADDR.reg = 0;
215 
216 	/* Set the automatic source / dest increment */
217 	switch (block->source_addr_adj) {
218 	case DMA_ADDR_ADJ_INCREMENT:
219 		desc->SRCADDR.reg = block->source_address + block->block_size;
220 		btctrl.bit.SRCINC = 1;
221 		break;
222 	case DMA_ADDR_ADJ_NO_CHANGE:
223 		desc->SRCADDR.reg = block->source_address;
224 		break;
225 	default:
226 		LOG_ERR("Invalid source increment");
227 		goto inval;
228 	}
229 
230 	switch (block->dest_addr_adj) {
231 	case DMA_ADDR_ADJ_INCREMENT:
232 		desc->DSTADDR.reg = block->dest_address + block->block_size;
233 		btctrl.bit.DSTINC = 1;
234 		break;
235 	case DMA_ADDR_ADJ_NO_CHANGE:
236 		desc->DSTADDR.reg = block->dest_address;
237 		break;
238 	default:
239 		LOG_ERR("Invalid destination increment");
240 		goto inval;
241 	}
242 
243 	btctrl.bit.VALID = 1;
244 	desc->BTCTRL = btctrl;
245 
246 	channel_control = &data->channels[channel];
247 	channel_control->cb = config->dma_callback;
248 	channel_control->user_data = config->user_data;
249 
250 	LOG_DBG("Configured channel %d for %08X to %08X (%u)",
251 		channel,
252 		block->source_address,
253 		block->dest_address,
254 		block->block_size);
255 
256 	irq_unlock(key);
257 	return 0;
258 
259 inval:
260 	irq_unlock(key);
261 	return -EINVAL;
262 }
263 
dma_sam0_start(const struct device * dev,uint32_t channel)264 static int dma_sam0_start(const struct device *dev, uint32_t channel)
265 {
266 	unsigned int key = irq_lock();
267 
268 	ARG_UNUSED(dev);
269 
270 #ifdef DMAC_CHID_ID
271 	DMA_REGS->CHID.reg = channel;
272 	DMA_REGS->CHCTRLA.reg = DMAC_CHCTRLA_ENABLE;
273 
274 	if (DMA_REGS->CHCTRLB.bit.TRIGSRC == 0) {
275 		/* Trigger via software */
276 		DMA_REGS->SWTRIGCTRL.reg = 1U << channel;
277 	}
278 
279 #else
280 	DmacChannel * chcfg = &DMA_REGS->Channel[channel];
281 
282 	chcfg->CHCTRLA.bit.ENABLE = 1;
283 
284 	if (chcfg->CHCTRLA.bit.TRIGSRC == 0) {
285 		/* Trigger via software */
286 		DMA_REGS->SWTRIGCTRL.reg = 1U << channel;
287 	}
288 #endif
289 
290 	irq_unlock(key);
291 
292 	return 0;
293 }
294 
dma_sam0_stop(const struct device * dev,uint32_t channel)295 static int dma_sam0_stop(const struct device *dev, uint32_t channel)
296 {
297 	unsigned int key = irq_lock();
298 
299 	ARG_UNUSED(dev);
300 
301 #ifdef DMAC_CHID_ID
302 	DMA_REGS->CHID.reg = channel;
303 	DMA_REGS->CHCTRLA.reg = 0;
304 #else
305 	DmacChannel * chcfg = &DMA_REGS->Channel[channel];
306 
307 	chcfg->CHCTRLA.bit.ENABLE = 0;
308 #endif
309 
310 	irq_unlock(key);
311 
312 	return 0;
313 }
314 
dma_sam0_reload(const struct device * dev,uint32_t channel,uint32_t src,uint32_t dst,size_t size)315 static int dma_sam0_reload(const struct device *dev, uint32_t channel,
316 			   uint32_t src, uint32_t dst, size_t size)
317 {
318 	struct dma_sam0_data *data = dev->data;
319 	DmacDescriptor *desc = &data->descriptors[channel];
320 	unsigned int key = irq_lock();
321 
322 	switch (desc->BTCTRL.bit.BEATSIZE) {
323 	case DMAC_BTCTRL_BEATSIZE_BYTE_Val:
324 		desc->BTCNT.reg = size;
325 		break;
326 	case DMAC_BTCTRL_BEATSIZE_HWORD_Val:
327 		desc->BTCNT.reg = size / 2U;
328 		break;
329 	case DMAC_BTCTRL_BEATSIZE_WORD_Val:
330 		desc->BTCNT.reg = size / 4U;
331 		break;
332 	default:
333 		goto inval;
334 	}
335 
336 	if (desc->BTCTRL.bit.SRCINC) {
337 		desc->SRCADDR.reg = src + size;
338 	} else {
339 		desc->SRCADDR.reg = src;
340 	}
341 
342 	if (desc->BTCTRL.bit.DSTINC) {
343 		desc->DSTADDR.reg = dst + size;
344 	} else {
345 		desc->DSTADDR.reg = dst;
346 	}
347 
348 	LOG_DBG("Reloaded channel %d for %08X to %08X (%u)",
349 		channel, src, dst, size);
350 
351 	irq_unlock(key);
352 	return 0;
353 
354 inval:
355 	irq_unlock(key);
356 	return -EINVAL;
357 }
358 
dma_sam0_get_status(const struct device * dev,uint32_t channel,struct dma_status * stat)359 static int dma_sam0_get_status(const struct device *dev, uint32_t channel,
360 			       struct dma_status *stat)
361 {
362 	struct dma_sam0_data *data = dev->data;
363 	uint32_t act;
364 
365 	if (channel >= DMAC_CH_NUM || stat == NULL) {
366 		return -EINVAL;
367 	}
368 
369 	act = DMA_REGS->ACTIVE.reg;
370 	if ((act & DMAC_ACTIVE_ABUSY) &&
371 	    ((act & DMAC_ACTIVE_ID_Msk) >> DMAC_ACTIVE_ID_Pos) == channel) {
372 		stat->busy = true;
373 		stat->pending_length = (act & DMAC_ACTIVE_BTCNT_Msk) >>
374 				       DMAC_ACTIVE_BTCNT_Pos;
375 	} else {
376 		stat->busy = false;
377 		stat->pending_length = data->descriptors_wb[channel].BTCNT.reg;
378 	}
379 
380 	switch (data->descriptors[channel].BTCTRL.bit.BEATSIZE) {
381 	case DMAC_BTCTRL_BEATSIZE_BYTE_Val:
382 		break;
383 	case DMAC_BTCTRL_BEATSIZE_HWORD_Val:
384 		stat->pending_length *= 2U;
385 		break;
386 	case DMAC_BTCTRL_BEATSIZE_WORD_Val:
387 		stat->pending_length *= 4U;
388 		break;
389 	default:
390 		return -EINVAL;
391 	}
392 
393 	return 0;
394 }
395 
396 #define DMA_SAM0_IRQ_CONNECT(n)						 \
397 	do {								 \
398 		IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, n, irq),		 \
399 			    DT_INST_IRQ_BY_IDX(0, n, priority),		 \
400 			    dma_sam0_isr, DEVICE_DT_INST_GET(0), 0);	 \
401 		irq_enable(DT_INST_IRQ_BY_IDX(0, n, irq));		 \
402 	} while (false)
403 
dma_sam0_init(const struct device * dev)404 static int dma_sam0_init(const struct device *dev)
405 {
406 	struct dma_sam0_data *data = dev->data;
407 
408 	/* Enable clocks. */
409 #ifdef MCLK
410 	MCLK->AHBMASK.bit.DMAC_ = 1;
411 #else
412 	PM->AHBMASK.bit.DMAC_ = 1;
413 	PM->APBBMASK.bit.DMAC_ = 1;
414 #endif
415 
416 	/* Set up the descriptor and write back addresses */
417 	DMA_REGS->BASEADDR.reg = (uintptr_t)&data->descriptors;
418 	DMA_REGS->WRBADDR.reg = (uintptr_t)&data->descriptors_wb;
419 
420 	/* Statically map each level to the same numeric priority */
421 	DMA_REGS->PRICTRL0.reg =
422 		DMAC_PRICTRL0_LVLPRI0(0) | DMAC_PRICTRL0_LVLPRI1(1) |
423 		DMAC_PRICTRL0_LVLPRI2(2) | DMAC_PRICTRL0_LVLPRI3(3);
424 
425 	/* Enable the unit and enable all priorities */
426 	DMA_REGS->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0x0F);
427 
428 #if DT_INST_IRQ_HAS_CELL(0, irq)
429 	DMA_SAM0_IRQ_CONNECT(0);
430 #endif
431 #if DT_INST_IRQ_HAS_IDX(0, 1)
432 	DMA_SAM0_IRQ_CONNECT(1);
433 #endif
434 #if DT_INST_IRQ_HAS_IDX(0, 2)
435 	DMA_SAM0_IRQ_CONNECT(2);
436 #endif
437 #if DT_INST_IRQ_HAS_IDX(0, 3)
438 	DMA_SAM0_IRQ_CONNECT(3);
439 #endif
440 #if DT_INST_IRQ_HAS_IDX(0, 4)
441 	DMA_SAM0_IRQ_CONNECT(4);
442 #endif
443 
444 	return 0;
445 }
446 
447 static struct dma_sam0_data dmac_data;
448 
449 static const struct dma_driver_api dma_sam0_api = {
450 	.config = dma_sam0_config,
451 	.start = dma_sam0_start,
452 	.stop = dma_sam0_stop,
453 	.reload = dma_sam0_reload,
454 	.get_status = dma_sam0_get_status,
455 };
456 
457 DEVICE_DT_INST_DEFINE(0, &dma_sam0_init, NULL,
458 		    &dmac_data, NULL, PRE_KERNEL_1,
459 		    CONFIG_DMA_INIT_PRIORITY, &dma_sam0_api);
460