]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/alpine-hal/al_hal_udma_main.c
Update to bmake-201802222
[FreeBSD/FreeBSD.git] / sys / contrib / alpine-hal / al_hal_udma_main.c
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         uint8_t rev_id = udma->rev_id;
74
75         if (udma->type == UDMA_TX) {
76                 struct unit_regs* tmp_unit_regs =
77                         (struct unit_regs*)udma->udma_regs;
78
79                 /* Setting the data fifo depth to 4K (256 strips of 16B)
80                  * This allows the UDMA to have 16 outstanding writes */
81                 if (rev_id >= AL_UDMA_REV_ID_2) {
82                         al_reg_write32_masked(&tmp_unit_regs->m2s.m2s_rd.data_cfg,
83                               UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_MASK,
84                               256 << UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_SHIFT);
85                 }
86
87                 /* set AXI timeout to 1M (~2.6 ms) */
88                 al_reg_write32(&tmp_unit_regs->gen.axi.cfg_1, 1000000);
89
90                 al_reg_write32(&tmp_unit_regs->m2s.m2s_comp.cfg_application_ack
91                                         , 0); /* Ack time out */
92         }
93         if (udma->type == UDMA_RX) {
94                 al_reg_write32(
95                         &udma->udma_regs->s2m.s2m_comp.cfg_application_ack, 0);
96                                         /* Ack time out */
97
98         }
99 }
100 /**
101  * misc queue configurations
102  *
103  * @param udma_q udma queue data structure
104  *
105  * @return 0
106  */
107 static int al_udma_q_config(struct al_udma_q *udma_q)
108 {
109         uint32_t *reg_addr;
110         uint32_t val;
111
112         if (udma_q->udma->type == UDMA_TX) {
113                 reg_addr = &udma_q->q_regs->m2s_q.rlimit.mask;
114
115                 val = al_reg_read32(reg_addr);
116                 // enable DMB
117                 val &= ~UDMA_M2S_Q_RATE_LIMIT_MASK_INTERNAL_PAUSE_DMB;
118                 al_reg_write32(reg_addr, val);
119         }
120         return 0;
121 }
122
123 /**
124  * set the queue's completion configuration register
125  *
126  * @param udma_q udma queue data structure
127  *
128  * @return 0
129  */
130 static int al_udma_q_config_compl(struct al_udma_q *udma_q)
131 {
132         uint32_t *reg_addr;
133         uint32_t val;
134
135         if (udma_q->udma->type == UDMA_TX)
136                 reg_addr = &udma_q->q_regs->m2s_q.comp_cfg;
137         else
138                 reg_addr = &udma_q->q_regs->s2m_q.comp_cfg;
139
140         val = al_reg_read32(reg_addr);
141
142         if (udma_q->flags & AL_UDMA_Q_FLAGS_NO_COMP_UPDATE)
143                 val &= ~UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
144         else
145                 val |= UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
146
147         if (udma_q->flags & AL_UDMA_Q_FLAGS_EN_COMP_COAL)
148                 val &= ~UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
149         else
150                 val |= UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
151
152         al_reg_write32(reg_addr, val);
153
154         /* set the completion queue size */
155         if (udma_q->udma->type == UDMA_RX) {
156                 val = al_reg_read32(
157                                 &udma_q->udma->udma_regs->s2m.s2m_comp.cfg_1c);
158                 val &= ~UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
159                 /* the register expects it to be in words */
160                 val |= (udma_q->cdesc_size >> 2)
161                                 & UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
162                 al_reg_write32(&udma_q->udma->udma_regs->s2m.s2m_comp.cfg_1c
163                                                         , val);
164         }
165         return 0;
166 }
167
168 /**
169  * reset the queues pointers (Head, Tail, etc) and set the base addresses
170  *
171  * @param udma_q udma queue data structure
172  */
173 static int al_udma_q_set_pointers(struct al_udma_q *udma_q)
174 {
175         /* reset the descriptors ring pointers */
176         /* assert descriptor base address aligned. */
177         al_assert((AL_ADDR_LOW(udma_q->desc_phy_base) &
178                    ~UDMA_M2S_Q_TDRBP_LOW_ADDR_MASK) == 0);
179         al_reg_write32(&udma_q->q_regs->rings.drbp_low,
180                        AL_ADDR_LOW(udma_q->desc_phy_base));
181         al_reg_write32(&udma_q->q_regs->rings.drbp_high,
182                        AL_ADDR_HIGH(udma_q->desc_phy_base));
183
184         al_reg_write32(&udma_q->q_regs->rings.drl, udma_q->size);
185
186         /* if completion ring update disabled */
187         if (udma_q->cdesc_base_ptr == NULL) {
188                 udma_q->flags |= AL_UDMA_Q_FLAGS_NO_COMP_UPDATE;
189         } else {
190                 /* reset the completion descriptors ring pointers */
191                 /* assert completion base address aligned. */
192                 al_assert((AL_ADDR_LOW(udma_q->cdesc_phy_base) &
193                            ~UDMA_M2S_Q_TCRBP_LOW_ADDR_MASK) == 0);
194                 al_reg_write32(&udma_q->q_regs->rings.crbp_low,
195                                AL_ADDR_LOW(udma_q->cdesc_phy_base));
196                 al_reg_write32(&udma_q->q_regs->rings.crbp_high,
197                                AL_ADDR_HIGH(udma_q->cdesc_phy_base));
198         }
199         al_udma_q_config_compl(udma_q);
200         return 0;
201 }
202
203 /**
204  * enable/disable udma queue
205  *
206  * @param udma_q udma queue data structure
207  * @param enable none zero value enables the queue, zero means disable
208  *
209  * @return 0
210  */
211 static int al_udma_q_enable(struct al_udma_q *udma_q, int enable)
212 {
213         uint32_t reg = al_reg_read32(&udma_q->q_regs->rings.cfg);
214
215         if (enable) {
216                 reg |= (UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
217                 udma_q->status = AL_QUEUE_ENABLED;
218         } else {
219                 reg &= ~(UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
220                 udma_q->status = AL_QUEUE_DISABLED;
221         }
222         al_reg_write32(&udma_q->q_regs->rings.cfg, reg);
223         return 0;
224 }
225
226
227 /************************ API functions ***************************************/
228
229 /* Initializations functions */
230 /*
231  * Initialize the udma engine
232  */
233 int al_udma_init(struct al_udma *udma, struct al_udma_params *udma_params)
234 {
235         int i;
236
237         al_assert(udma);
238
239         if (udma_params->num_of_queues > DMA_MAX_Q) {
240                 al_err("udma: invalid num_of_queues parameter\n");
241                 return -EINVAL;
242         }
243
244         udma->type = udma_params->type;
245         udma->num_of_queues = udma_params->num_of_queues;
246         udma->gen_regs = &udma_params->udma_regs_base->gen;
247
248         if (udma->type == UDMA_TX)
249                 udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->m2s;
250         else
251                 udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->s2m;
252
253         udma->rev_id = al_udma_get_revision(udma_params->udma_regs_base);
254
255         if (udma_params->name == NULL)
256                 udma->name = "";
257         else
258                 udma->name = udma_params->name;
259
260         udma->state = UDMA_DISABLE;
261         for (i = 0; i < DMA_MAX_Q; i++) {
262                 udma->udma_q[i].status = AL_QUEUE_NOT_INITIALIZED;
263         }
264         /* initialize configuration registers to correct values */
265         al_udma_set_defaults(udma);
266         al_dbg("udma [%s] initialized. base %p\n", udma->name,
267                 udma->udma_regs);
268         return 0;
269 }
270
271 /*
272  * Initialize the udma queue data structure
273  */
274 int al_udma_q_init(struct al_udma *udma, uint32_t qid,
275                                         struct al_udma_q_params *q_params)
276 {
277         struct al_udma_q *udma_q;
278
279         al_assert(udma);
280         al_assert(q_params);
281
282         if (qid >= udma->num_of_queues) {
283                 al_err("udma: invalid queue id (%d)\n", qid);
284                 return -EINVAL;
285         }
286
287         if (udma->udma_q[qid].status == AL_QUEUE_ENABLED) {
288                 al_err("udma: queue (%d) already enabled!\n", qid);
289                 return -EIO;
290         }
291
292         if (q_params->size < AL_UDMA_MIN_Q_SIZE) {
293                 al_err("udma: queue (%d) size too small\n", qid);
294                 return -EINVAL;
295         }
296
297         if (q_params->size > AL_UDMA_MAX_Q_SIZE) {
298                 al_err("udma: queue (%d) size too large\n", qid);
299                 return -EINVAL;
300         }
301
302         if (q_params->size & (q_params->size - 1)) {
303                 al_err("udma: queue (%d) size (%d) must be power of 2\n",
304                          q_params->size, qid);
305                 return -EINVAL;
306         }
307
308         udma_q = &udma->udma_q[qid];
309         /* set the queue's regs base address */
310         if (udma->type == UDMA_TX)
311                 udma_q->q_regs = (union udma_q_regs __iomem *)
312                                         &udma->udma_regs->m2s.m2s_q[qid];
313         else
314                 udma_q->q_regs = (union udma_q_regs __iomem *)
315                                         &udma->udma_regs->s2m.s2m_q[qid];
316
317         udma_q->adapter_rev_id = q_params->adapter_rev_id;
318         udma_q->size = q_params->size;
319         udma_q->size_mask = q_params->size - 1;
320         udma_q->desc_base_ptr = q_params->desc_base;
321         udma_q->desc_phy_base = q_params->desc_phy_base;
322         udma_q->cdesc_base_ptr = q_params->cdesc_base;
323         udma_q->cdesc_phy_base = q_params->cdesc_phy_base;
324         udma_q->cdesc_size = q_params->cdesc_size;
325
326         udma_q->next_desc_idx = 0;
327         udma_q->next_cdesc_idx = 0;
328         udma_q->end_cdesc_ptr = (uint8_t *) udma_q->cdesc_base_ptr +
329             (udma_q->size - 1) * udma_q->cdesc_size;
330         udma_q->comp_head_idx = 0;
331         udma_q->comp_head_ptr = (union al_udma_cdesc *)udma_q->cdesc_base_ptr;
332         udma_q->desc_ring_id = AL_UDMA_INITIAL_RING_ID;
333         udma_q->comp_ring_id = AL_UDMA_INITIAL_RING_ID;
334 #if 0
335         udma_q->desc_ctrl_bits = AL_UDMA_INITIAL_RING_ID <<
336                                                 AL_M2S_DESC_RING_ID_SHIFT;
337 #endif
338         udma_q->pkt_crnt_descs = 0;
339         udma_q->flags = 0;
340         udma_q->status = AL_QUEUE_DISABLED;
341         udma_q->udma = udma;
342         udma_q->qid = qid;
343
344         /* start hardware configuration: */
345         al_udma_q_config(udma_q);
346         /* reset the queue pointers */
347         al_udma_q_set_pointers(udma_q);
348
349         /* enable the q */
350         al_udma_q_enable(udma_q, 1);
351
352         al_dbg("udma [%s %d]: %s q init. size 0x%x\n"
353                         "  desc ring info: phys base 0x%llx virt base %p)",
354                         udma_q->udma->name, udma_q->qid,
355                         udma->type == UDMA_TX ? "Tx" : "Rx",
356                         q_params->size,
357                         (unsigned long long)q_params->desc_phy_base,
358                         q_params->desc_base);
359         al_dbg("  cdesc ring info: phys base 0x%llx virt base %p entry size 0x%x",
360                         (unsigned long long)q_params->cdesc_phy_base,
361                         q_params->cdesc_base,
362                         q_params->cdesc_size);
363
364         return 0;
365 }
366
367 /*
368  * Reset a udma queue
369  */
370 int al_udma_q_reset(struct al_udma_q *udma_q)
371 {
372         unsigned int remaining_time = AL_UDMA_Q_RST_TOUT;
373         uint32_t *status_reg;
374         uint32_t *dcp_reg;
375         uint32_t *crhp_reg;
376         uint32_t *q_sw_ctrl_reg;
377
378         al_assert(udma_q);
379
380         /* De-assert scheduling and prefetch */
381         al_udma_q_enable(udma_q, 0);
382
383         /* Wait for scheduling and prefetch to stop */
384         status_reg = &udma_q->q_regs->rings.status;
385
386         while (remaining_time) {
387                 uint32_t status = al_reg_read32(status_reg);
388
389                 if (!(status & (UDMA_M2S_Q_STATUS_PREFETCH |
390                                                 UDMA_M2S_Q_STATUS_SCHEDULER)))
391                         break;
392
393                 remaining_time--;
394                 al_udelay(1);
395         }
396
397         if (!remaining_time) {
398                 al_err("udma [%s %d]: %s timeout waiting for prefetch and "
399                         "scheduler disable\n", udma_q->udma->name, udma_q->qid,
400                         __func__);
401                 return -ETIMEDOUT;
402         }
403
404         /* Wait for the completion queue to reach to the same pointer as the
405          * prefetch stopped at ([TR]DCP == [TR]CRHP) */
406         dcp_reg = &udma_q->q_regs->rings.dcp;
407         crhp_reg = &udma_q->q_regs->rings.crhp;
408
409         while (remaining_time) {
410                 uint32_t dcp = al_reg_read32(dcp_reg);
411                 uint32_t crhp = al_reg_read32(crhp_reg);
412
413                 if (dcp == crhp)
414                         break;
415
416                 remaining_time--;
417                 al_udelay(1);
418         };
419
420         if (!remaining_time) {
421                 al_err("udma [%s %d]: %s timeout waiting for dcp==crhp\n",
422                         udma_q->udma->name, udma_q->qid, __func__);
423                 return -ETIMEDOUT;
424         }
425
426         /* Assert the queue reset */
427         if (udma_q->udma->type == UDMA_TX)
428                 q_sw_ctrl_reg = &udma_q->q_regs->m2s_q.q_sw_ctrl;
429         else
430                 q_sw_ctrl_reg = &udma_q->q_regs->s2m_q.q_sw_ctrl;
431
432         al_reg_write32(q_sw_ctrl_reg, UDMA_M2S_Q_SW_CTRL_RST_Q);
433
434         return 0;
435 }
436
437 /*
438  * return (by reference) a pointer to a specific queue date structure.
439  */
440 int al_udma_q_handle_get(struct al_udma *udma, uint32_t qid,
441                                                 struct al_udma_q **q_handle)
442 {
443
444         al_assert(udma);
445         al_assert(q_handle);
446
447         if (unlikely(qid >= udma->num_of_queues)) {
448                 al_err("udma [%s]: invalid queue id (%d)\n", udma->name, qid);
449                 return -EINVAL;
450         }
451         *q_handle = &udma->udma_q[qid];
452         return 0;
453 }
454
455 /*
456  * Change the UDMA's state
457  */
458 int al_udma_state_set(struct al_udma *udma, enum al_udma_state state)
459 {
460         uint32_t reg;
461
462         al_assert(udma != NULL);
463         if (state == udma->state)
464                 al_dbg("udma [%s]: requested state identical to "
465                         "current state (%d)\n", udma->name, state);
466
467         al_dbg("udma [%s]: change state from (%s) to (%s)\n",
468                  udma->name, al_udma_states_name[udma->state],
469                  al_udma_states_name[state]);
470
471         reg = 0;
472         switch (state) {
473         case UDMA_DISABLE:
474                 reg |= UDMA_M2S_CHANGE_STATE_DIS;
475                 break;
476         case UDMA_NORMAL:
477                 reg |= UDMA_M2S_CHANGE_STATE_NORMAL;
478                 break;
479         case UDMA_ABORT:
480                 reg |= UDMA_M2S_CHANGE_STATE_ABORT;
481                 break;
482         default:
483                 al_err("udma: invalid state (%d)\n", state);
484                 return -EINVAL;
485         }
486
487         if (udma->type == UDMA_TX)
488                 al_reg_write32(&udma->udma_regs->m2s.m2s.change_state, reg);
489         else
490                 al_reg_write32(&udma->udma_regs->s2m.s2m.change_state, reg);
491
492         udma->state = state;
493         return 0;
494 }
495
496 /*
497  * return the current UDMA hardware state
498  */
499 enum al_udma_state al_udma_state_get(struct al_udma *udma)
500 {
501         uint32_t state_reg;
502         uint32_t comp_ctrl;
503         uint32_t stream_if;
504         uint32_t data_rd;
505         uint32_t desc_pref;
506
507         if (udma->type == UDMA_TX)
508                 state_reg = al_reg_read32(&udma->udma_regs->m2s.m2s.state);
509         else
510                 state_reg = al_reg_read32(&udma->udma_regs->s2m.s2m.state);
511
512         comp_ctrl = AL_REG_FIELD_GET(state_reg,
513                                      UDMA_M2S_STATE_COMP_CTRL_MASK,
514                                      UDMA_M2S_STATE_COMP_CTRL_SHIFT);
515         stream_if = AL_REG_FIELD_GET(state_reg,
516                                      UDMA_M2S_STATE_STREAM_IF_MASK,
517                                      UDMA_M2S_STATE_STREAM_IF_SHIFT);
518         data_rd = AL_REG_FIELD_GET(state_reg,
519                                    UDMA_M2S_STATE_DATA_RD_CTRL_MASK,
520                                    UDMA_M2S_STATE_DATA_RD_CTRL_SHIFT);
521         desc_pref = AL_REG_FIELD_GET(state_reg,
522                                      UDMA_M2S_STATE_DESC_PREF_MASK,
523                                      UDMA_M2S_STATE_DESC_PREF_SHIFT);
524
525         al_assert(comp_ctrl != UDMA_STATE_RESERVED);
526         al_assert(stream_if != UDMA_STATE_RESERVED);
527         al_assert(data_rd != UDMA_STATE_RESERVED);
528         al_assert(desc_pref != UDMA_STATE_RESERVED);
529
530         /* if any of the states is abort then return abort */
531         if ((comp_ctrl == UDMA_STATE_ABORT) || (stream_if == UDMA_STATE_ABORT)
532                         || (data_rd == UDMA_STATE_ABORT)
533                         || (desc_pref == UDMA_STATE_ABORT))
534                 return UDMA_ABORT;
535
536         /* if any of the states is normal then return normal */
537         if ((comp_ctrl == UDMA_STATE_NORMAL)
538                         || (stream_if == UDMA_STATE_NORMAL)
539                         || (data_rd == UDMA_STATE_NORMAL)
540                         || (desc_pref == UDMA_STATE_NORMAL))
541                 return UDMA_NORMAL;
542
543         return UDMA_IDLE;
544 }
545
546 /*
547  * Action handling
548  */
549
550 /*
551  * get next completed packet from completion ring of the queue
552  */
553 uint32_t al_udma_cdesc_packet_get(
554         struct al_udma_q                *udma_q,
555         volatile union al_udma_cdesc    **cdesc)
556 {
557         uint32_t count;
558         volatile union al_udma_cdesc *curr;
559         uint32_t comp_flags;
560
561         /* this function requires the completion ring update */
562         al_assert(!(udma_q->flags & AL_UDMA_Q_FLAGS_NO_COMP_UPDATE));
563
564         /* comp_head points to the last comp desc that was processed */
565         curr = udma_q->comp_head_ptr;
566         comp_flags = swap32_from_le(curr->al_desc_comp_tx.ctrl_meta);
567
568         /* check if the completion descriptor is new */
569         if (unlikely(al_udma_new_cdesc(udma_q, comp_flags) == AL_FALSE))
570                 return 0;
571         /* if new desc found, increment the current packets descriptors */
572         count = udma_q->pkt_crnt_descs + 1;
573         while (!cdesc_is_last(comp_flags)) {
574                 curr = al_cdesc_next_update(udma_q, curr);
575                 comp_flags = swap32_from_le(curr->al_desc_comp_tx.ctrl_meta);
576                 if (unlikely(al_udma_new_cdesc(udma_q, comp_flags)
577                                                                 == AL_FALSE)) {
578                         /* the current packet here doesn't have all  */
579                         /* descriptors completed. log the current desc */
580                         /* location and number of completed descriptors so */
581                         /*  far. then return */
582                         udma_q->pkt_crnt_descs = count;
583                         udma_q->comp_head_ptr = curr;
584                         return 0;
585                 }
586                 count++;
587                 /* check against max descs per packet. */
588                 al_assert(count <= udma_q->size);
589         }
590         /* return back the first descriptor of the packet */
591         *cdesc = al_udma_cdesc_idx_to_ptr(udma_q, udma_q->next_cdesc_idx);
592         udma_q->pkt_crnt_descs = 0;
593         udma_q->comp_head_ptr = al_cdesc_next_update(udma_q, curr);
594
595         al_dbg("udma [%s %d]: packet completed. first desc %p (ixd 0x%x)"
596                  " descs %d\n", udma_q->udma->name, udma_q->qid, *cdesc,
597                  udma_q->next_cdesc_idx, count);
598
599         return count;
600 }
601
602 /** @} end of UDMA group */