1294838Szbb/*-
2294838Szbb*******************************************************************************
3294838SzbbCopyright (C) 2015 Annapurna Labs Ltd.
4294838Szbb
5294838SzbbThis file may be licensed under the terms of the Annapurna Labs Commercial
6294838SzbbLicense Agreement.
7294838Szbb
8294838SzbbAlternatively, this file can be distributed under the terms of the GNU General
9294838SzbbPublic License V2 as published by the Free Software Foundation and can be
10294838Szbbfound at http://www.gnu.org/licenses/gpl-2.0.html
11294838Szbb
12294838SzbbAlternatively, redistribution and use in source and binary forms, with or
13294838Szbbwithout modification, are permitted provided that the following conditions are
14294838Szbbmet:
15294838Szbb
16294838Szbb    *     Redistributions of source code must retain the above copyright notice,
17294838Szbbthis list of conditions and the following disclaimer.
18294838Szbb
19294838Szbb    *     Redistributions in binary form must reproduce the above copyright
20294838Szbbnotice, this list of conditions and the following disclaimer in
21294838Szbbthe documentation and/or other materials provided with the
22294838Szbbdistribution.
23294838Szbb
24294838SzbbTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25294838SzbbANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26294838SzbbWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27294838SzbbDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
28294838SzbbANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29294838Szbb(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30294838SzbbLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
31294838SzbbANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32294838Szbb(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33294838SzbbSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34294838Szbb
35294838Szbb*******************************************************************************/
36294838Szbb
37294838Szbb/**
38294838Szbb *  @{
39294838Szbb * @file   al_hal_udma_main.c
40294838Szbb *
41294838Szbb * @brief  Universal DMA HAL driver for main functions (initialization, data path)
42294838Szbb *
43294838Szbb */
44294838Szbb
45294838Szbb#include <al_hal_udma.h>
46294838Szbb#include <al_hal_udma_config.h>
47294838Szbb
48294838Szbb#define AL_UDMA_Q_RST_TOUT	10000	/* Queue reset timeout [uSecs] */
49294838Szbb
50294838Szbb#define UDMA_STATE_IDLE		0x0
51294838Szbb#define UDMA_STATE_NORMAL	0x1
52294838Szbb#define UDMA_STATE_ABORT	0x2
53294838Szbb#define UDMA_STATE_RESERVED	0x3
54294838Szbb
55294838Szbbconst char *const al_udma_states_name[] = {
56294838Szbb	"Disable",
57294838Szbb	"Idle",
58294838Szbb	"Normal",
59294838Szbb	"Abort",
60294838Szbb	"Reset"
61294838Szbb};
62294838Szbb
63294838Szbb#define AL_UDMA_INITIAL_RING_ID	1
64294838Szbb
65294838Szbb/*  dma_q flags */
66294838Szbb#define AL_UDMA_Q_FLAGS_IGNORE_RING_ID	AL_BIT(0)
67294838Szbb#define AL_UDMA_Q_FLAGS_NO_COMP_UPDATE	AL_BIT(1)
68294838Szbb#define AL_UDMA_Q_FLAGS_EN_COMP_COAL	AL_BIT(2)
69294838Szbb
70294838Szbb
71294838Szbbstatic void al_udma_set_defaults(struct al_udma *udma)
72294838Szbb{
73294838Szbb	uint32_t tmp;
74294838Szbb	uint8_t rev_id = udma->rev_id;
75294838Szbb
76294838Szbb	if (udma->type == UDMA_TX) {
77294838Szbb		struct unit_regs* tmp_unit_regs =
78294838Szbb			(struct unit_regs*)udma->udma_regs;
79294838Szbb
80294838Szbb		/* Setting the data fifo depth to 4K (256 strips of 16B)
81294838Szbb		 * This allows the UDMA to have 16 outstanding writes */
82294838Szbb		if (rev_id >= AL_UDMA_REV_ID_2) {
83294838Szbb			al_reg_write32_masked(&tmp_unit_regs->m2s.m2s_rd.data_cfg,
84294838Szbb			      UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_MASK,
85294838Szbb			      256 << UDMA_M2S_RD_DATA_CFG_DATA_FIFO_DEPTH_SHIFT);
86294838Szbb		}
87294838Szbb
88294838Szbb		if (rev_id == AL_UDMA_REV_ID_0)
89294838Szbb			/* disable AXI timeout for M0*/
90294838Szbb			al_reg_write32(&tmp_unit_regs->gen.axi.cfg_1, 0);
91294838Szbb		else
92294838Szbb			/* set AXI timeout to 1M (~2.6 ms) */
93294838Szbb			al_reg_write32(&tmp_unit_regs->gen.axi.cfg_1, 1000000);
94294838Szbb
95294838Szbb		al_reg_write32(&tmp_unit_regs->m2s.m2s_comp.cfg_application_ack
96294838Szbb					, 0); /* Ack time out */
97294838Szbb
98294838Szbb
99294838Szbb		if (rev_id == AL_UDMA_REV_ID_0) {
100294838Szbb			tmp = al_reg_read32(&udma->udma_regs->m2s.axi_m2s.desc_wr_cfg_1);
101294838Szbb			tmp &= ~UDMA_AXI_M2S_DESC_WR_CFG_1_MAX_AXI_BEATS_MASK;
102294838Szbb			tmp |= 4 << UDMA_AXI_M2S_DESC_WR_CFG_1_MAX_AXI_BEATS_SHIFT;
103294838Szbb			al_reg_write32(&udma->udma_regs->m2s.axi_m2s.desc_wr_cfg_1
104294838Szbb									, tmp);
105294838Szbb		}
106294838Szbb
107294838Szbb	}
108294838Szbb	if (udma->type == UDMA_RX) {
109294838Szbb		al_reg_write32(
110294838Szbb			&udma->udma_regs->s2m.s2m_comp.cfg_application_ack, 0);
111294838Szbb					/* Ack time out */
112294838Szbb
113294838Szbb	}
114294838Szbb}
115294838Szbb/**
116294838Szbb * misc queue configurations
117294838Szbb *
118294838Szbb * @param udma_q udma queue data structure
119294838Szbb *
120294838Szbb * @return 0
121294838Szbb */
122294838Szbbstatic int al_udma_q_config(struct al_udma_q *udma_q)
123294838Szbb{
124294838Szbb	uint32_t *reg_addr;
125294838Szbb	uint32_t val;
126294838Szbb
127294838Szbb	if (udma_q->udma->type == UDMA_TX) {
128294838Szbb		reg_addr = &udma_q->q_regs->m2s_q.rlimit.mask;
129294838Szbb
130294838Szbb		val = al_reg_read32(reg_addr);
131294838Szbb		// enable DMB
132294838Szbb		val &= ~UDMA_M2S_Q_RATE_LIMIT_MASK_INTERNAL_PAUSE_DMB;
133294838Szbb		al_reg_write32(reg_addr, val);
134294838Szbb	}
135294838Szbb	return 0;
136294838Szbb}
137294838Szbb
138294838Szbb/**
139294838Szbb * set the queue's completion configuration register
140294838Szbb *
141294838Szbb * @param udma_q udma queue data structure
142294838Szbb *
143294838Szbb * @return 0
144294838Szbb */
145294838Szbbstatic int al_udma_q_config_compl(struct al_udma_q *udma_q)
146294838Szbb{
147294838Szbb	uint32_t *reg_addr;
148294838Szbb	uint32_t val;
149294838Szbb
150294838Szbb	if (udma_q->udma->type == UDMA_TX)
151294838Szbb		reg_addr = &udma_q->q_regs->m2s_q.comp_cfg;
152294838Szbb	else
153294838Szbb		reg_addr = &udma_q->q_regs->s2m_q.comp_cfg;
154294838Szbb
155294838Szbb	val = al_reg_read32(reg_addr);
156294838Szbb
157294838Szbb	if (udma_q->flags & AL_UDMA_Q_FLAGS_NO_COMP_UPDATE)
158294838Szbb		val &= ~UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
159294838Szbb	else
160294838Szbb		val |= UDMA_M2S_Q_COMP_CFG_EN_COMP_RING_UPDATE;
161294838Szbb
162294838Szbb	if (udma_q->flags & AL_UDMA_Q_FLAGS_EN_COMP_COAL)
163294838Szbb		val &= ~UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
164294838Szbb	else
165294838Szbb		val |= UDMA_M2S_Q_COMP_CFG_DIS_COMP_COAL;
166294838Szbb
167294838Szbb	al_reg_write32(reg_addr, val);
168294838Szbb
169294838Szbb	/* set the completion queue size */
170294838Szbb	if (udma_q->udma->type == UDMA_RX) {
171294838Szbb		val = al_reg_read32(
172294838Szbb				&udma_q->udma->udma_regs->s2m.s2m_comp.cfg_1c);
173294838Szbb		val &= ~UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
174294838Szbb		/* the register expects it to be in words */
175294838Szbb		val |= (udma_q->cdesc_size >> 2)
176294838Szbb				& UDMA_S2M_COMP_CFG_1C_DESC_SIZE_MASK;
177294838Szbb		al_reg_write32(&udma_q->udma->udma_regs->s2m.s2m_comp.cfg_1c
178294838Szbb							, val);
179294838Szbb	}
180294838Szbb	return 0;
181294838Szbb}
182294838Szbb
183294838Szbb/**
184294838Szbb * reset the queues pointers (Head, Tail, etc) and set the base addresses
185294838Szbb *
186294838Szbb * @param udma_q udma queue data structure
187294838Szbb */
188294838Szbbstatic int al_udma_q_set_pointers(struct al_udma_q *udma_q)
189294838Szbb{
190294838Szbb	/* reset the descriptors ring pointers */
191294838Szbb	/* assert descriptor base address aligned. */
192294838Szbb	al_assert((AL_ADDR_LOW(udma_q->desc_phy_base) &
193294838Szbb		   ~UDMA_M2S_Q_TDRBP_LOW_ADDR_MASK) == 0);
194294838Szbb	al_reg_write32(&udma_q->q_regs->rings.drbp_low,
195294838Szbb		       AL_ADDR_LOW(udma_q->desc_phy_base));
196294838Szbb	al_reg_write32(&udma_q->q_regs->rings.drbp_high,
197294838Szbb		       AL_ADDR_HIGH(udma_q->desc_phy_base));
198294838Szbb
199294838Szbb	al_reg_write32(&udma_q->q_regs->rings.drl, udma_q->size);
200294838Szbb
201294838Szbb	/* if completion ring update disabled */
202294838Szbb	if (udma_q->cdesc_base_ptr == NULL) {
203294838Szbb		udma_q->flags |= AL_UDMA_Q_FLAGS_NO_COMP_UPDATE;
204294838Szbb	} else {
205294838Szbb		/* reset the completion descriptors ring pointers */
206294838Szbb		/* assert completion base address aligned. */
207294838Szbb		al_assert((AL_ADDR_LOW(udma_q->cdesc_phy_base) &
208294838Szbb			   ~UDMA_M2S_Q_TCRBP_LOW_ADDR_MASK) == 0);
209294838Szbb		al_reg_write32(&udma_q->q_regs->rings.crbp_low,
210294838Szbb			       AL_ADDR_LOW(udma_q->cdesc_phy_base));
211294838Szbb		al_reg_write32(&udma_q->q_regs->rings.crbp_high,
212294838Szbb			       AL_ADDR_HIGH(udma_q->cdesc_phy_base));
213294838Szbb	}
214294838Szbb	al_udma_q_config_compl(udma_q);
215294838Szbb	return 0;
216294838Szbb}
217294838Szbb
218294838Szbb/**
219294838Szbb * enable/disable udma queue
220294838Szbb *
221294838Szbb * @param udma_q udma queue data structure
222294838Szbb * @param enable none zero value enables the queue, zero means disable
223294838Szbb *
224294838Szbb * @return 0
225294838Szbb */
226294838Szbbstatic int al_udma_q_enable(struct al_udma_q *udma_q, int enable)
227294838Szbb{
228294838Szbb	uint32_t reg = al_reg_read32(&udma_q->q_regs->rings.cfg);
229294838Szbb
230294838Szbb	if (enable) {
231294838Szbb		reg |= (UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
232294838Szbb		udma_q->status = AL_QUEUE_ENABLED;
233294838Szbb	} else {
234294838Szbb		reg &= ~(UDMA_M2S_Q_CFG_EN_PREF | UDMA_M2S_Q_CFG_EN_SCHEDULING);
235294838Szbb		udma_q->status = AL_QUEUE_DISABLED;
236294838Szbb	}
237294838Szbb	al_reg_write32(&udma_q->q_regs->rings.cfg, reg);
238294838Szbb	return 0;
239294838Szbb}
240294838Szbb
241294838Szbb
242294838Szbb/************************ API functions ***************************************/
243294838Szbb
244294838Szbb/* Initializations functions */
245294838Szbb/*
246294838Szbb * Initialize the udma engine
247294838Szbb */
248294838Szbbint al_udma_init(struct al_udma *udma, struct al_udma_params *udma_params)
249294838Szbb{
250294838Szbb	int i;
251294838Szbb
252294838Szbb	al_assert(udma);
253294838Szbb
254294838Szbb	if (udma_params->num_of_queues > DMA_MAX_Q) {
255294838Szbb		al_err("udma: invalid num_of_queues parameter\n");
256294838Szbb		return -EINVAL;
257294838Szbb	}
258294838Szbb
259294838Szbb	udma->type = udma_params->type;
260294838Szbb	udma->num_of_queues = udma_params->num_of_queues;
261294838Szbb	udma->gen_regs = &udma_params->udma_regs_base->gen;
262294838Szbb
263294838Szbb	if (udma->type == UDMA_TX)
264294838Szbb		udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->m2s;
265294838Szbb	else
266294838Szbb		udma->udma_regs = (union udma_regs *)&udma_params->udma_regs_base->s2m;
267294838Szbb
268294838Szbb	udma->rev_id = al_udma_get_revision(udma_params->udma_regs_base);
269294838Szbb
270294838Szbb	if (udma_params->name == NULL)
271294838Szbb		udma->name = "";
272294838Szbb	else
273294838Szbb		udma->name = udma_params->name;
274294838Szbb
275294838Szbb	udma->state = UDMA_DISABLE;
276294838Szbb	for (i = 0; i < DMA_MAX_Q; i++) {
277294838Szbb		udma->udma_q[i].status = AL_QUEUE_NOT_INITIALIZED;
278294838Szbb	}
279294838Szbb	/* initialize configuration registers to correct values */
280294838Szbb	al_udma_set_defaults(udma);
281294838Szbb	al_dbg("udma [%s] initialized. base %p\n", udma->name,
282294838Szbb		udma->udma_regs);
283294838Szbb	return 0;
284294838Szbb}
285294838Szbb
286294838Szbb/*
287294838Szbb * Initialize the udma queue data structure
288294838Szbb */
289294838Szbbint al_udma_q_init(struct al_udma *udma, uint32_t qid,
290294838Szbb					struct al_udma_q_params *q_params)
291294838Szbb{
292294838Szbb	struct al_udma_q *udma_q;
293294838Szbb
294294838Szbb	al_assert(udma);
295294838Szbb	al_assert(q_params);
296294838Szbb
297294838Szbb	if (qid >= udma->num_of_queues) {
298294838Szbb		al_err("udma: invalid queue id (%d)\n", qid);
299294838Szbb		return -EINVAL;
300294838Szbb	}
301294838Szbb
302294838Szbb	if (udma->udma_q[qid].status == AL_QUEUE_ENABLED) {
303294838Szbb		al_err("udma: queue (%d) already enabled!\n", qid);
304294838Szbb		return -EIO;
305294838Szbb	}
306294838Szbb
307294838Szbb	if (q_params->size < AL_UDMA_MIN_Q_SIZE) {
308294838Szbb		al_err("udma: queue (%d) size too small\n", qid);
309294838Szbb		return -EINVAL;
310294838Szbb	}
311294838Szbb
312294838Szbb	if (q_params->size > AL_UDMA_MAX_Q_SIZE) {
313294838Szbb		al_err("udma: queue (%d) size too large\n", qid);
314294838Szbb		return -EINVAL;
315294838Szbb	}
316294838Szbb
317294838Szbb	if (q_params->size & (q_params->size - 1)) {
318294838Szbb		al_err("udma: queue (%d) size (%d) must be power of 2\n",
319294838Szbb			 q_params->size, qid);
320294838Szbb		return -EINVAL;
321294838Szbb	}
322294838Szbb
323294838Szbb	udma_q = &udma->udma_q[qid];
324294838Szbb	/* set the queue's regs base address */
325294838Szbb	if (udma->type == UDMA_TX)
326294838Szbb		udma_q->q_regs = (union udma_q_regs __iomem *)
327294838Szbb					&udma->udma_regs->m2s.m2s_q[qid];
328294838Szbb	else
329294838Szbb		udma_q->q_regs = (union udma_q_regs __iomem *)
330294838Szbb					&udma->udma_regs->s2m.s2m_q[qid];
331294838Szbb
332294838Szbb	udma_q->adapter_rev_id = q_params->adapter_rev_id;
333294838Szbb	udma_q->size = q_params->size;
334294838Szbb	udma_q->size_mask = q_params->size - 1;
335294838Szbb	udma_q->desc_base_ptr = q_params->desc_base;
336294838Szbb	udma_q->desc_phy_base = q_params->desc_phy_base;
337294838Szbb	udma_q->cdesc_base_ptr = q_params->cdesc_base;
338294838Szbb	udma_q->cdesc_phy_base = q_params->cdesc_phy_base;
339294838Szbb	udma_q->cdesc_size = q_params->cdesc_size;
340294838Szbb
341294838Szbb	udma_q->next_desc_idx = 0;
342294838Szbb	udma_q->next_cdesc_idx = 0;
343294838Szbb	udma_q->end_cdesc_ptr = (uint8_t *) udma_q->cdesc_base_ptr +
344294838Szbb	    (udma_q->size - 1) * udma_q->cdesc_size;
345294838Szbb	udma_q->comp_head_idx = 0;
346294838Szbb	udma_q->comp_head_ptr = (union al_udma_cdesc *)udma_q->cdesc_base_ptr;
347294838Szbb	udma_q->desc_ring_id = AL_UDMA_INITIAL_RING_ID;
348294838Szbb	udma_q->comp_ring_id = AL_UDMA_INITIAL_RING_ID;
349294838Szbb#if 0
350294838Szbb	udma_q->desc_ctrl_bits = AL_UDMA_INITIAL_RING_ID <<
351294838Szbb						AL_M2S_DESC_RING_ID_SHIFT;
352294838Szbb#endif
353294838Szbb	udma_q->pkt_crnt_descs = 0;
354294838Szbb	udma_q->flags = 0;
355294838Szbb	udma_q->status = AL_QUEUE_DISABLED;
356294838Szbb	udma_q->udma = udma;
357294838Szbb	udma_q->qid = qid;
358294838Szbb
359294838Szbb	/* start hardware configuration: */
360294838Szbb	al_udma_q_config(udma_q);
361294838Szbb	/* reset the queue pointers */
362294838Szbb	al_udma_q_set_pointers(udma_q);
363294838Szbb
364294838Szbb	/* enable the q */
365294838Szbb	al_udma_q_enable(udma_q, 1);
366294838Szbb
367294838Szbb	al_dbg("udma [%s %d]: %s q init. size 0x%x\n"
368294838Szbb			"  desc ring info: phys base 0x%llx virt base %p\n"
369294838Szbb			"  cdesc ring info: phys base 0x%llx virt base %p "
370294838Szbb				"entry size 0x%x",
371294838Szbb			udma_q->udma->name, udma_q->qid,
372294838Szbb			udma->type == UDMA_TX ? "Tx" : "Rx",
373294838Szbb			q_params->size,
374294838Szbb			(unsigned long long)q_params->desc_phy_base,
375294838Szbb			q_params->desc_base,
376294838Szbb			(unsigned long long)q_params->cdesc_phy_base,
377294838Szbb			q_params->cdesc_base,
378294838Szbb			q_params->cdesc_size);
379294838Szbb
380294838Szbb	return 0;
381294838Szbb}
382294838Szbb
383294838Szbb/*
384294838Szbb * Reset a udma queue
385294838Szbb */
386294838Szbbint al_udma_q_reset(struct al_udma_q *udma_q)
387294838Szbb{
388294838Szbb	unsigned int remaining_time = AL_UDMA_Q_RST_TOUT;
389294838Szbb	uint32_t *status_reg;
390294838Szbb	uint32_t *dcp_reg;
391294838Szbb	uint32_t *crhp_reg;
392294838Szbb	uint32_t *q_sw_ctrl_reg;
393294838Szbb
394294838Szbb	al_assert(udma_q);
395294838Szbb
396294838Szbb	/* De-assert scheduling and prefetch */
397294838Szbb	al_udma_q_enable(udma_q, 0);
398294838Szbb
399294838Szbb	/* Wait for scheduling and prefetch to stop */
400294838Szbb	status_reg = &udma_q->q_regs->rings.status;
401294838Szbb
402294838Szbb	while (remaining_time) {
403294838Szbb		uint32_t status = al_reg_read32(status_reg);
404294838Szbb
405294838Szbb		if (!(status & (UDMA_M2S_Q_STATUS_PREFETCH |
406294838Szbb						UDMA_M2S_Q_STATUS_SCHEDULER)))
407294838Szbb			break;
408294838Szbb
409294838Szbb		remaining_time--;
410294838Szbb		al_udelay(1);
411294838Szbb	}
412294838Szbb
413294838Szbb	if (!remaining_time) {
414294838Szbb		al_err("udma [%s %d]: %s timeout waiting for prefetch and "
415294838Szbb			"scheduler disable\n", udma_q->udma->name, udma_q->qid,
416294838Szbb			__func__);
417294838Szbb		return -ETIMEDOUT;
418294838Szbb	}
419294838Szbb
420294838Szbb	/* Wait for the completion queue to reach to the same pointer as the
421294838Szbb	 * prefetch stopped at ([TR]DCP == [TR]CRHP) */
422294838Szbb	dcp_reg = &udma_q->q_regs->rings.dcp;
423294838Szbb	crhp_reg = &udma_q->q_regs->rings.crhp;
424294838Szbb
425294838Szbb	while (remaining_time) {
426294838Szbb		uint32_t dcp = al_reg_read32(dcp_reg);
427294838Szbb		uint32_t crhp = al_reg_read32(crhp_reg);
428294838Szbb
429294838Szbb		if (dcp == crhp)
430294838Szbb			break;
431294838Szbb
432294838Szbb		remaining_time--;
433294838Szbb		al_udelay(1);
434294838Szbb	};
435294838Szbb
436294838Szbb	if (!remaining_time) {
437294838Szbb		al_err("udma [%s %d]: %s timeout waiting for dcp==crhp\n",
438294838Szbb			udma_q->udma->name, udma_q->qid, __func__);
439294838Szbb		return -ETIMEDOUT;
440294838Szbb	}
441294838Szbb
442294838Szbb	/* Assert the queue reset */
443294838Szbb	if (udma_q->udma->type == UDMA_TX)
444294838Szbb		q_sw_ctrl_reg = &udma_q->q_regs->m2s_q.q_sw_ctrl;
445294838Szbb	else
446294838Szbb		q_sw_ctrl_reg = &udma_q->q_regs->s2m_q.q_sw_ctrl;
447294838Szbb
448294838Szbb	al_reg_write32(q_sw_ctrl_reg, UDMA_M2S_Q_SW_CTRL_RST_Q);
449294838Szbb
450294838Szbb	return 0;
451294838Szbb}
452294838Szbb
453294838Szbb/*
454294838Szbb * return (by reference) a pointer to a specific queue date structure.
455294838Szbb */
456294838Szbbint al_udma_q_handle_get(struct al_udma *udma, uint32_t qid,
457294838Szbb						struct al_udma_q **q_handle)
458294838Szbb{
459294838Szbb
460294838Szbb	al_assert(udma);
461294838Szbb	al_assert(q_handle);
462294838Szbb
463294838Szbb	if (unlikely(qid >= udma->num_of_queues)) {
464294838Szbb		al_err("udma [%s]: invalid queue id (%d)\n", udma->name, qid);
465294838Szbb		return -EINVAL;
466294838Szbb	}
467294838Szbb	*q_handle = &udma->udma_q[qid];
468294838Szbb	return 0;
469294838Szbb}
470294838Szbb
471294838Szbb/*
472294838Szbb * Change the UDMA's state
473294838Szbb */
474294838Szbbint al_udma_state_set(struct al_udma *udma, enum al_udma_state state)
475294838Szbb{
476294838Szbb	uint32_t reg;
477294838Szbb
478294838Szbb	al_assert(udma != NULL);
479294838Szbb	if (state == udma->state)
480294838Szbb		al_dbg("udma [%s]: requested state identical to "
481294838Szbb			"current state (%d)\n", udma->name, state);
482294838Szbb
483294838Szbb	al_dbg("udma [%s]: change state from (%s) to (%s)\n",
484294838Szbb		 udma->name, al_udma_states_name[udma->state],
485294838Szbb		 al_udma_states_name[state]);
486294838Szbb
487294838Szbb	reg = 0;
488294838Szbb	switch (state) {
489294838Szbb	case UDMA_DISABLE:
490294838Szbb		reg |= UDMA_M2S_CHANGE_STATE_DIS;
491294838Szbb		break;
492294838Szbb	case UDMA_NORMAL:
493294838Szbb		reg |= UDMA_M2S_CHANGE_STATE_NORMAL;
494294838Szbb		break;
495294838Szbb	case UDMA_ABORT:
496294838Szbb		reg |= UDMA_M2S_CHANGE_STATE_ABORT;
497294838Szbb		break;
498294838Szbb	default:
499294838Szbb		al_err("udma: invalid state (%d)\n", state);
500294838Szbb		return -EINVAL;
501294838Szbb	}
502294838Szbb
503294838Szbb	if (udma->type == UDMA_TX)
504294838Szbb		al_reg_write32(&udma->udma_regs->m2s.m2s.change_state, reg);
505294838Szbb	else
506294838Szbb		al_reg_write32(&udma->udma_regs->s2m.s2m.change_state, reg);
507294838Szbb
508294838Szbb	udma->state = state;
509294838Szbb	return 0;
510294838Szbb}
511294838Szbb
512294838Szbb/*
513294838Szbb * return the current UDMA hardware state
514294838Szbb */
515294838Szbbenum al_udma_state al_udma_state_get(struct al_udma *udma)
516294838Szbb{
517294838Szbb	uint32_t state_reg;
518294838Szbb	uint32_t comp_ctrl;
519294838Szbb	uint32_t stream_if;
520294838Szbb	uint32_t data_rd;
521294838Szbb	uint32_t desc_pref;
522294838Szbb
523294838Szbb	if (udma->type == UDMA_TX)
524294838Szbb		state_reg = al_reg_read32(&udma->udma_regs->m2s.m2s.state);
525294838Szbb	else
526294838Szbb		state_reg = al_reg_read32(&udma->udma_regs->s2m.s2m.state);
527294838Szbb
528294838Szbb	comp_ctrl = AL_REG_FIELD_GET(state_reg,
529294838Szbb				     UDMA_M2S_STATE_COMP_CTRL_MASK,
530294838Szbb				     UDMA_M2S_STATE_COMP_CTRL_SHIFT);
531294838Szbb	stream_if = AL_REG_FIELD_GET(state_reg,
532294838Szbb				     UDMA_M2S_STATE_STREAM_IF_MASK,
533294838Szbb				     UDMA_M2S_STATE_STREAM_IF_SHIFT);
534294838Szbb	data_rd = AL_REG_FIELD_GET(state_reg,
535294838Szbb				   UDMA_M2S_STATE_DATA_RD_CTRL_MASK,
536294838Szbb				   UDMA_M2S_STATE_DATA_RD_CTRL_SHIFT);
537294838Szbb	desc_pref = AL_REG_FIELD_GET(state_reg,
538294838Szbb				     UDMA_M2S_STATE_DESC_PREF_MASK,
539294838Szbb				     UDMA_M2S_STATE_DESC_PREF_SHIFT);
540294838Szbb
541294838Szbb	al_assert(comp_ctrl != UDMA_STATE_RESERVED);
542294838Szbb	al_assert(stream_if != UDMA_STATE_RESERVED);
543294838Szbb	al_assert(data_rd != UDMA_STATE_RESERVED);
544294838Szbb	al_assert(desc_pref != UDMA_STATE_RESERVED);
545294838Szbb
546294838Szbb	/* if any of the states is abort then return abort */
547294838Szbb	if ((comp_ctrl == UDMA_STATE_ABORT) || (stream_if == UDMA_STATE_ABORT)
548294838Szbb			|| (data_rd == UDMA_STATE_ABORT)
549294838Szbb			|| (desc_pref == UDMA_STATE_ABORT))
550294838Szbb		return UDMA_ABORT;
551294838Szbb
552294838Szbb	/* if any of the states is normal then return normal */
553294838Szbb	if ((comp_ctrl == UDMA_STATE_NORMAL)
554294838Szbb			|| (stream_if == UDMA_STATE_NORMAL)
555294838Szbb			|| (data_rd == UDMA_STATE_NORMAL)
556294838Szbb			|| (desc_pref == UDMA_STATE_NORMAL))
557294838Szbb		return UDMA_NORMAL;
558294838Szbb
559294838Szbb	return UDMA_IDLE;
560294838Szbb}
561294838Szbb
562294838Szbb/*
563294838Szbb * Action handling
564294838Szbb */
565294838Szbb
566294838Szbb/*
567294838Szbb * get next completed packet from completion ring of the queue
568294838Szbb */
569294838Szbbuint32_t al_udma_cdesc_packet_get(
570294838Szbb	struct al_udma_q		*udma_q,
571294838Szbb	volatile union al_udma_cdesc	**cdesc)
572294838Szbb{
573294838Szbb	uint32_t count;
574294838Szbb	volatile union al_udma_cdesc *curr;
575294838Szbb	uint32_t comp_flags;
576294838Szbb
577294838Szbb	/* this function requires the completion ring update */
578294838Szbb	al_assert(!(udma_q->flags & AL_UDMA_Q_FLAGS_NO_COMP_UPDATE));
579294838Szbb
580294838Szbb	/* comp_head points to the last comp desc that was processed */
581294838Szbb	curr = udma_q->comp_head_ptr;
582294838Szbb	comp_flags = swap32_from_le(curr->al_desc_comp_tx.ctrl_meta);
583294838Szbb
584294838Szbb	/* check if the completion descriptor is new */
585294838Szbb	if (unlikely(al_udma_new_cdesc(udma_q, comp_flags) == AL_FALSE))
586294838Szbb		return 0;
587294838Szbb	/* if new desc found, increment the current packets descriptors */
588294838Szbb	count = udma_q->pkt_crnt_descs + 1;
589294838Szbb	while (!cdesc_is_last(comp_flags)) {
590294838Szbb		curr = al_cdesc_next_update(udma_q, curr);
591294838Szbb		comp_flags = swap32_from_le(curr->al_desc_comp_tx.ctrl_meta);
592294838Szbb		if (unlikely(al_udma_new_cdesc(udma_q, comp_flags)
593294838Szbb								== AL_FALSE)) {
594294838Szbb			/* the current packet here doesn't have all  */
595294838Szbb			/* descriptors completed. log the current desc */
596294838Szbb			/* location and number of completed descriptors so */
597294838Szbb			/*  far. then return */
598294838Szbb			udma_q->pkt_crnt_descs = count;
599294838Szbb			udma_q->comp_head_ptr = curr;
600294838Szbb			return 0;
601294838Szbb		}
602294838Szbb		count++;
603294838Szbb		/* check against max descs per packet. */
604294838Szbb		al_assert(count <= udma_q->size);
605294838Szbb	}
606294838Szbb	/* return back the first descriptor of the packet */
607294838Szbb	*cdesc = al_udma_cdesc_idx_to_ptr(udma_q, udma_q->next_cdesc_idx);
608294838Szbb	udma_q->pkt_crnt_descs = 0;
609294838Szbb	udma_q->comp_head_ptr = al_cdesc_next_update(udma_q, curr);
610294838Szbb
611294838Szbb	al_dbg("udma [%s %d]: packet completed. first desc %p (ixd 0x%x)"
612294838Szbb		 " descs %d\n", udma_q->udma->name, udma_q->qid, *cdesc,
613294838Szbb		 udma_q->next_cdesc_idx, count);
614294838Szbb
615294838Szbb	return count;
616294838Szbb}
617294838Szbb
618294838Szbb/** @} end of UDMA group */
619