]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/alpine-hal/al_hal_udma_main.c
cdn-patch: mark unionfs as jail-safe
[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         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 */