xref: /freebsd/sys/contrib/alpine-hal/al_hal_udma_main.c (revision 1f4bcc459a76b7aa664f3fd557684cd0ba6da352)
1 /*-
2 *******************************************************************************
3 Copyright (C) 2015 Annapurna Labs Ltd.
4 
5 This file may be licensed under the terms of the Annapurna Labs Commercial
6 License Agreement.
7 
8 Alternatively, this file can be distributed under the terms of the GNU General
9 Public License V2 as published by the Free Software Foundation and can be
10 found at http://www.gnu.org/licenses/gpl-2.0.html
11 
12 Alternatively, redistribution and use in source and binary forms, with or
13 without modification, are permitted provided that the following conditions are
14 met:
15 
16     *     Redistributions of source code must retain the above copyright notice,
17 this list of conditions and the following disclaimer.
18 
19     *     Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in
21 the documentation and/or other materials provided with the
22 distribution.
23 
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
28 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
31 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 
35 *******************************************************************************/
36 
37 /**
38  *  @{
39  * @file   al_hal_udma_main.c
40  *
41  * @brief  Universal DMA HAL driver for main functions (initialization, data path)
42  *
43  */
44 
45 #include <al_hal_udma.h>
46 #include <al_hal_udma_config.h>
47 
48 #define AL_UDMA_Q_RST_TOUT	10000	/* Queue reset timeout [uSecs] */
49 
50 #define UDMA_STATE_IDLE		0x0
51 #define UDMA_STATE_NORMAL	0x1
52 #define UDMA_STATE_ABORT	0x2
53 #define UDMA_STATE_RESERVED	0x3
54 
55 const char *const al_udma_states_name[] = {
56 	"Disable",
57 	"Idle",
58 	"Normal",
59 	"Abort",
60 	"Reset"
61 };
62 
63 #define AL_UDMA_INITIAL_RING_ID	1
64 
65 /*  dma_q flags */
66 #define AL_UDMA_Q_FLAGS_IGNORE_RING_ID	AL_BIT(0)
67 #define AL_UDMA_Q_FLAGS_NO_COMP_UPDATE	AL_BIT(1)
68 #define AL_UDMA_Q_FLAGS_EN_COMP_COAL	AL_BIT(2)
69 
70 
71 static void al_udma_set_defaults(struct al_udma *udma)
72 {
73 	uint32_t tmp;
74 	uint8_t rev_id = udma->rev_id;
75 
76 	if (udma->type == UDMA_TX) {
77 		struct unit_regs* tmp_unit_regs =
78 			(struct unit_regs*)udma->udma_regs;
79 
80 		/* Setting the data fifo depth to 4K (256 strips of 16B)
81 		 * This allows the UDMA to have 16 outstanding writes */
82 		if (rev_id >= AL_UDMA_REV_ID_2) {
83 			al_reg_write32_masked(&tmp_unit_regs->m2s.m2s_rd.data_cfg,
84 			      UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_MASK,
85 			      256 << UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_SHIFT);
86 		}
87 
88 		if (rev_id == AL_UDMA_REV_ID_0)
89 			/* disable AXI timeout for M0*/
90 			al_reg_write32(&tmp_unit_regs->gen.axi.cfg_1, 0);
91 		else
92 			/* set AXI timeout to 1M (~2.6 ms) */
93 			al_reg_write32(&tmp_unit_regs->gen.axi.cfg_1, 1000000);
94 
95 		al_reg_write32(&tmp_unit_regs->m2s.m2s_comp.cfg_application_ack
96 					, 0); /* Ack time out */
97 
98 
99 		if (rev_id == AL_UDMA_REV_ID_0) {
100 			tmp = al_reg_read32(&udma->udma_regs->m2s.axi_m2s.desc_wr_cfg_1);
101 			tmp &= ~UDMA_AXI_M2S_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK;
102 			tmp |= 4 << UDMA_AXI_M2S_DESC_WR_CFG_1_MAX_AXI_BEATS_SHIFT;
103 			al_reg_write32(&udma->udma_regs->m2s.axi_m2s.desc_wr_cfg_1
104 									, tmp);
105 		}
106 
107 	}
108 	if (udma->type == UDMA_RX) {
109 		al_reg_write32(
110 			&udma->udma_regs->s2m.s2m_comp.cfg_application_ack, 0);
111 					/* Ack time out */
112 
113 	}
114 }
115 /**
116  * misc queue configurations
117  *
118  * @param udma_q udma queue data structure
119  *
120  * @return 0
121  */
122 static int al_udma_q_config(struct al_udma_q *udma_q)
123 {
124 	uint32_t *reg_addr;
125 	uint32_t val;
126 
127 	if (udma_q->udma->type == UDMA_TX) {
128 		reg_addr = &udma_q->q_regs->m2s_q.rlimit.mask;
129 
130 		val = al_reg_read32(reg_addr);
131 		// enable DMB
132 		val &= ~UDMA_M2S_Q_RATE_LIMIT_MASK_INTERNAL_PAUSE_DMB;
133 		al_reg_write32(reg_addr, val);
134 	}
135 	return 0;
136 }
137 
138 /**
139  * set the queue's completion configuration register
140  *
141  * @param udma_q udma queue data structure
142  *
143  * @return 0
144  */
145 static int al_udma_q_config_compl(struct al_udma_q *udma_q)
146 {
147 	uint32_t *reg_addr;
148 	uint32_t val;
149 
150 	if (udma_q->udma->type == UDMA_TX)
151 		reg_addr = &udma_q->q_regs->m2s_q.comp_cfg;
152 	else
153 		reg_addr = &udma_q->q_regs->s2m_q.comp_cfg;
154 
155 	val = al_reg_read32(reg_addr);
156 
157 	if (udma_q->flags & AL_UDMA_Q_FLAGS_NO_COMP_UPDATE)
158 		val &= ~UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
159 	else
160 		val |= UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
161 
162 	if (udma_q->flags & AL_UDMA_Q_FLAGS_EN_COMP_COAL)
163 		val &= ~UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
164 	else
165 		val |= UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
166 
167 	al_reg_write32(reg_addr, val);
168 
169 	/* set the completion queue size */
170 	if (udma_q->udma->type == UDMA_RX) {
171 		val = al_reg_read32(
172 				&udma_q->udma->udma_regs->s2m.s2m_comp.cfg_1c);
173 		val &= ~UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
174 		/* the register expects it to be in words */
175 		val |= (udma_q->cdesc_size >> 2)
176 				& UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
177 		al_reg_write32(&udma_q->udma->udma_regs->s2m.s2m_comp.cfg_1c
178 							, val);
179 	}
180 	return 0;
181 }
182 
183 /**
184  * reset the queues pointers (Head, Tail, etc) and set the base addresses
185  *
186  * @param udma_q udma queue data structure
187  */
188 static int al_udma_q_set_pointers(struct al_udma_q *udma_q)
189 {
190 	/* reset the descriptors ring pointers */
191 	/* assert descriptor base address aligned. */
192 	al_assert((AL_ADDR_LOW(udma_q->desc_phy_base) &
193 		   ~UDMA_M2S_Q_TDRBP_LOW_ADDR_MASK) == 0);
194 	al_reg_write32(&udma_q->q_regs->rings.drbp_low,
195 		       AL_ADDR_LOW(udma_q->desc_phy_base));
196 	al_reg_write32(&udma_q->q_regs->rings.drbp_high,
197 		       AL_ADDR_HIGH(udma_q->desc_phy_base));
198 
199 	al_reg_write32(&udma_q->q_regs->rings.drl, udma_q->size);
200 
201 	/* if completion ring update disabled */
202 	if (udma_q->cdesc_base_ptr == NULL) {
203 		udma_q->flags |= AL_UDMA_Q_FLAGS_NO_COMP_UPDATE;
204 	} else {
205 		/* reset the completion descriptors ring pointers */
206 		/* assert completion base address aligned. */
207 		al_assert((AL_ADDR_LOW(udma_q->cdesc_phy_base) &
208 			   ~UDMA_M2S_Q_TCRBP_LOW_ADDR_MASK) == 0);
209 		al_reg_write32(&udma_q->q_regs->rings.crbp_low,
210 			       AL_ADDR_LOW(udma_q->cdesc_phy_base));
211 		al_reg_write32(&udma_q->q_regs->rings.crbp_high,
212 			       AL_ADDR_HIGH(udma_q->cdesc_phy_base));
213 	}
214 	al_udma_q_config_compl(udma_q);
215 	return 0;
216 }
217 
218 /**
219  * enable/disable udma queue
220  *
221  * @param udma_q udma queue data structure
222  * @param enable none zero value enables the queue, zero means disable
223  *
224  * @return 0
225  */
226 static int al_udma_q_enable(struct al_udma_q *udma_q, int enable)
227 {
228 	uint32_t reg = al_reg_read32(&udma_q->q_regs->rings.cfg);
229 
230 	if (enable) {
231 		reg |= (UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
232 		udma_q->status = AL_QUEUE_ENABLED;
233 	} else {
234 		reg &= ~(UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
235 		udma_q->status = AL_QUEUE_DISABLED;
236 	}
237 	al_reg_write32(&udma_q->q_regs->rings.cfg, reg);
238 	return 0;
239 }
240 
241 
242 /************************ API functions ***************************************/
243 
244 /* Initializations functions */
245 /*
246  * Initialize the udma engine
247  */
248 int al_udma_init(struct al_udma *udma, struct al_udma_params *udma_params)
249 {
250 	int i;
251 
252 	al_assert(udma);
253 
254 	if (udma_params->num_of_queues > DMA_MAX_Q) {
255 		al_err("udma: invalid num_of_queues parameter\n");
256 		return -EINVAL;
257 	}
258 
259 	udma->type = udma_params->type;
260 	udma->num_of_queues = udma_params->num_of_queues;
261 	udma->gen_regs = &udma_params->udma_regs_base->gen;
262 
263 	if (udma->type == UDMA_TX)
264 		udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->m2s;
265 	else
266 		udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->s2m;
267 
268 	udma->rev_id = al_udma_get_revision(udma_params->udma_regs_base);
269 
270 	if (udma_params->name == NULL)
271 		udma->name = "";
272 	else
273 		udma->name = udma_params->name;
274 
275 	udma->state = UDMA_DISABLE;
276 	for (i = 0; i < DMA_MAX_Q; i++) {
277 		udma->udma_q[i].status = AL_QUEUE_NOT_INITIALIZED;
278 	}
279 	/* initialize configuration registers to correct values */
280 	al_udma_set_defaults(udma);
281 	al_dbg("udma [%s] initialized. base %p\n", udma->name,
282 		udma->udma_regs);
283 	return 0;
284 }
285 
286 /*
287  * Initialize the udma queue data structure
288  */
289 int al_udma_q_init(struct al_udma *udma, uint32_t qid,
290 					struct al_udma_q_params *q_params)
291 {
292 	struct al_udma_q *udma_q;
293 
294 	al_assert(udma);
295 	al_assert(q_params);
296 
297 	if (qid >= udma->num_of_queues) {
298 		al_err("udma: invalid queue id (%d)\n", qid);
299 		return -EINVAL;
300 	}
301 
302 	if (udma->udma_q[qid].status == AL_QUEUE_ENABLED) {
303 		al_err("udma: queue (%d) already enabled!\n", qid);
304 		return -EIO;
305 	}
306 
307 	if (q_params->size < AL_UDMA_MIN_Q_SIZE) {
308 		al_err("udma: queue (%d) size too small\n", qid);
309 		return -EINVAL;
310 	}
311 
312 	if (q_params->size > AL_UDMA_MAX_Q_SIZE) {
313 		al_err("udma: queue (%d) size too large\n", qid);
314 		return -EINVAL;
315 	}
316 
317 	if (q_params->size & (q_params->size - 1)) {
318 		al_err("udma: queue (%d) size (%d) must be power of 2\n",
319 			 q_params->size, qid);
320 		return -EINVAL;
321 	}
322 
323 	udma_q = &udma->udma_q[qid];
324 	/* set the queue's regs base address */
325 	if (udma->type == UDMA_TX)
326 		udma_q->q_regs = (union udma_q_regs __iomem *)
327 					&udma->udma_regs->m2s.m2s_q[qid];
328 	else
329 		udma_q->q_regs = (union udma_q_regs __iomem *)
330 					&udma->udma_regs->s2m.s2m_q[qid];
331 
332 	udma_q->adapter_rev_id = q_params->adapter_rev_id;
333 	udma_q->size = q_params->size;
334 	udma_q->size_mask = q_params->size - 1;
335 	udma_q->desc_base_ptr = q_params->desc_base;
336 	udma_q->desc_phy_base = q_params->desc_phy_base;
337 	udma_q->cdesc_base_ptr = q_params->cdesc_base;
338 	udma_q->cdesc_phy_base = q_params->cdesc_phy_base;
339 	udma_q->cdesc_size = q_params->cdesc_size;
340 
341 	udma_q->next_desc_idx = 0;
342 	udma_q->next_cdesc_idx = 0;
343 	udma_q->end_cdesc_ptr = (uint8_t *) udma_q->cdesc_base_ptr +
344 	    (udma_q->size - 1) * udma_q->cdesc_size;
345 	udma_q->comp_head_idx = 0;
346 	udma_q->comp_head_ptr = (union al_udma_cdesc *)udma_q->cdesc_base_ptr;
347 	udma_q->desc_ring_id = AL_UDMA_INITIAL_RING_ID;
348 	udma_q->comp_ring_id = AL_UDMA_INITIAL_RING_ID;
349 #if 0
350 	udma_q->desc_ctrl_bits = AL_UDMA_INITIAL_RING_ID <<
351 						AL_M2S_DESC_RING_ID_SHIFT;
352 #endif
353 	udma_q->pkt_crnt_descs = 0;
354 	udma_q->flags = 0;
355 	udma_q->status = AL_QUEUE_DISABLED;
356 	udma_q->udma = udma;
357 	udma_q->qid = qid;
358 
359 	/* start hardware configuration: */
360 	al_udma_q_config(udma_q);
361 	/* reset the queue pointers */
362 	al_udma_q_set_pointers(udma_q);
363 
364 	/* enable the q */
365 	al_udma_q_enable(udma_q, 1);
366 
367 	al_dbg("udma [%s %d]: %s q init. size 0x%x\n"
368 			"  desc ring info: phys base 0x%llx virt base %p\n"
369 			"  cdesc ring info: phys base 0x%llx virt base %p "
370 				"entry size 0x%x",
371 			udma_q->udma->name, udma_q->qid,
372 			udma->type == UDMA_TX ? "Tx" : "Rx",
373 			q_params->size,
374 			(unsigned long long)q_params->desc_phy_base,
375 			q_params->desc_base,
376 			(unsigned long long)q_params->cdesc_phy_base,
377 			q_params->cdesc_base,
378 			q_params->cdesc_size);
379 
380 	return 0;
381 }
382 
383 /*
384  * Reset a udma queue
385  */
386 int al_udma_q_reset(struct al_udma_q *udma_q)
387 {
388 	unsigned int remaining_time = AL_UDMA_Q_RST_TOUT;
389 	uint32_t *status_reg;
390 	uint32_t *dcp_reg;
391 	uint32_t *crhp_reg;
392 	uint32_t *q_sw_ctrl_reg;
393 
394 	al_assert(udma_q);
395 
396 	/* De-assert scheduling and prefetch */
397 	al_udma_q_enable(udma_q, 0);
398 
399 	/* Wait for scheduling and prefetch to stop */
400 	status_reg = &udma_q->q_regs->rings.status;
401 
402 	while (remaining_time) {
403 		uint32_t status = al_reg_read32(status_reg);
404 
405 		if (!(status & (UDMA_M2S_Q_STATUS_PREFETCH |
406 						UDMA_M2S_Q_STATUS_SCHEDULER)))
407 			break;
408 
409 		remaining_time--;
410 		al_udelay(1);
411 	}
412 
413 	if (!remaining_time) {
414 		al_err("udma [%s %d]: %s timeout waiting for prefetch and "
415 			"scheduler disable\n", udma_q->udma->name, udma_q->qid,
416 			__func__);
417 		return -ETIMEDOUT;
418 	}
419 
420 	/* Wait for the completion queue to reach to the same pointer as the
421 	 * prefetch stopped at ([TR]DCP == [TR]CRHP) */
422 	dcp_reg = &udma_q->q_regs->rings.dcp;
423 	crhp_reg = &udma_q->q_regs->rings.crhp;
424 
425 	while (remaining_time) {
426 		uint32_t dcp = al_reg_read32(dcp_reg);
427 		uint32_t crhp = al_reg_read32(crhp_reg);
428 
429 		if (dcp == crhp)
430 			break;
431 
432 		remaining_time--;
433 		al_udelay(1);
434 	};
435 
436 	if (!remaining_time) {
437 		al_err("udma [%s %d]: %s timeout waiting for dcp==crhp\n",
438 			udma_q->udma->name, udma_q->qid, __func__);
439 		return -ETIMEDOUT;
440 	}
441 
442 	/* Assert the queue reset */
443 	if (udma_q->udma->type == UDMA_TX)
444 		q_sw_ctrl_reg = &udma_q->q_regs->m2s_q.q_sw_ctrl;
445 	else
446 		q_sw_ctrl_reg = &udma_q->q_regs->s2m_q.q_sw_ctrl;
447 
448 	al_reg_write32(q_sw_ctrl_reg, UDMA_M2S_Q_SW_CTRL_RST_Q);
449 
450 	return 0;
451 }
452 
453 /*
454  * return (by reference) a pointer to a specific queue date structure.
455  */
456 int al_udma_q_handle_get(struct al_udma *udma, uint32_t qid,
457 						struct al_udma_q **q_handle)
458 {
459 
460 	al_assert(udma);
461 	al_assert(q_handle);
462 
463 	if (unlikely(qid >= udma->num_of_queues)) {
464 		al_err("udma [%s]: invalid queue id (%d)\n", udma->name, qid);
465 		return -EINVAL;
466 	}
467 	*q_handle = &udma->udma_q[qid];
468 	return 0;
469 }
470 
471 /*
472  * Change the UDMA's state
473  */
474 int al_udma_state_set(struct al_udma *udma, enum al_udma_state state)
475 {
476 	uint32_t reg;
477 
478 	al_assert(udma != NULL);
479 	if (state == udma->state)
480 		al_dbg("udma [%s]: requested state identical to "
481 			"current state (%d)\n", udma->name, state);
482 
483 	al_dbg("udma [%s]: change state from (%s) to (%s)\n",
484 		 udma->name, al_udma_states_name[udma->state],
485 		 al_udma_states_name[state]);
486 
487 	reg = 0;
488 	switch (state) {
489 	case UDMA_DISABLE:
490 		reg |= UDMA_M2S_CHANGE_STATE_DIS;
491 		break;
492 	case UDMA_NORMAL:
493 		reg |= UDMA_M2S_CHANGE_STATE_NORMAL;
494 		break;
495 	case UDMA_ABORT:
496 		reg |= UDMA_M2S_CHANGE_STATE_ABORT;
497 		break;
498 	default:
499 		al_err("udma: invalid state (%d)\n", state);
500 		return -EINVAL;
501 	}
502 
503 	if (udma->type == UDMA_TX)
504 		al_reg_write32(&udma->udma_regs->m2s.m2s.change_state, reg);
505 	else
506 		al_reg_write32(&udma->udma_regs->s2m.s2m.change_state, reg);
507 
508 	udma->state = state;
509 	return 0;
510 }
511 
512 /*
513  * return the current UDMA hardware state
514  */
515 enum al_udma_state al_udma_state_get(struct al_udma *udma)
516 {
517 	uint32_t state_reg;
518 	uint32_t comp_ctrl;
519 	uint32_t stream_if;
520 	uint32_t data_rd;
521 	uint32_t desc_pref;
522 
523 	if (udma->type == UDMA_TX)
524 		state_reg = al_reg_read32(&udma->udma_regs->m2s.m2s.state);
525 	else
526 		state_reg = al_reg_read32(&udma->udma_regs->s2m.s2m.state);
527 
528 	comp_ctrl = AL_REG_FIELD_GET(state_reg,
529 				     UDMA_M2S_STATE_COMP_CTRL_MASK,
530 				     UDMA_M2S_STATE_COMP_CTRL_SHIFT);
531 	stream_if = AL_REG_FIELD_GET(state_reg,
532 				     UDMA_M2S_STATE_STREAM_IF_MASK,
533 				     UDMA_M2S_STATE_STREAM_IF_SHIFT);
534 	data_rd = AL_REG_FIELD_GET(state_reg,
535 				   UDMA_M2S_STATE_DATA_RD_CTRL_MASK,
536 				   UDMA_M2S_STATE_DATA_RD_CTRL_SHIFT);
537 	desc_pref = AL_REG_FIELD_GET(state_reg,
538 				     UDMA_M2S_STATE_DESC_PREF_MASK,
539 				     UDMA_M2S_STATE_DESC_PREF_SHIFT);
540 
541 	al_assert(comp_ctrl != UDMA_STATE_RESERVED);
542 	al_assert(stream_if != UDMA_STATE_RESERVED);
543 	al_assert(data_rd != UDMA_STATE_RESERVED);
544 	al_assert(desc_pref != UDMA_STATE_RESERVED);
545 
546 	/* if any of the states is abort then return abort */
547 	if ((comp_ctrl == UDMA_STATE_ABORT) || (stream_if == UDMA_STATE_ABORT)
548 			|| (data_rd == UDMA_STATE_ABORT)
549 			|| (desc_pref == UDMA_STATE_ABORT))
550 		return UDMA_ABORT;
551 
552 	/* if any of the states is normal then return normal */
553 	if ((comp_ctrl == UDMA_STATE_NORMAL)
554 			|| (stream_if == UDMA_STATE_NORMAL)
555 			|| (data_rd == UDMA_STATE_NORMAL)
556 			|| (desc_pref == UDMA_STATE_NORMAL))
557 		return UDMA_NORMAL;
558 
559 	return UDMA_IDLE;
560 }
561 
562 /*
563  * Action handling
564  */
565 
566 /*
567  * get next completed packet from completion ring of the queue
568  */
569 uint32_t al_udma_cdesc_packet_get(
570 	struct al_udma_q		*udma_q,
571 	volatile union al_udma_cdesc	**cdesc)
572 {
573 	uint32_t count;
574 	volatile union al_udma_cdesc *curr;
575 	uint32_t comp_flags;
576 
577 	/* this function requires the completion ring update */
578 	al_assert(!(udma_q->flags & AL_UDMA_Q_FLAGS_NO_COMP_UPDATE));
579 
580 	/* comp_head points to the last comp desc that was processed */
581 	curr = udma_q->comp_head_ptr;
582 	comp_flags = swap32_from_le(curr->al_desc_comp_tx.ctrl_meta);
583 
584 	/* check if the completion descriptor is new */
585 	if (unlikely(al_udma_new_cdesc(udma_q, comp_flags) == AL_FALSE))
586 		return 0;
587 	/* if new desc found, increment the current packets descriptors */
588 	count = udma_q->pkt_crnt_descs + 1;
589 	while (!cdesc_is_last(comp_flags)) {
590 		curr = al_cdesc_next_update(udma_q, curr);
591 		comp_flags = swap32_from_le(curr->al_desc_comp_tx.ctrl_meta);
592 		if (unlikely(al_udma_new_cdesc(udma_q, comp_flags)
593 								== AL_FALSE)) {
594 			/* the current packet here doesn't have all  */
595 			/* descriptors completed. log the current desc */
596 			/* location and number of completed descriptors so */
597 			/*  far. then return */
598 			udma_q->pkt_crnt_descs = count;
599 			udma_q->comp_head_ptr = curr;
600 			return 0;
601 		}
602 		count++;
603 		/* check against max descs per packet. */
604 		al_assert(count <= udma_q->size);
605 	}
606 	/* return back the first descriptor of the packet */
607 	*cdesc = al_udma_cdesc_idx_to_ptr(udma_q, udma_q->next_cdesc_idx);
608 	udma_q->pkt_crnt_descs = 0;
609 	udma_q->comp_head_ptr = al_cdesc_next_update(udma_q, curr);
610 
611 	al_dbg("udma [%s %d]: packet completed. first desc %p (ixd 0x%x)"
612 		 " descs %d\n", udma_q->udma->name, udma_q->qid, *cdesc,
613 		 udma_q->next_cdesc_idx, count);
614 
615 	return count;
616 }
617 
618 /** @} end of UDMA group */
619