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