162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Renesas USB driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011 Renesas Solutions Corp.
662306a36Sopenharmony_ci * Copyright (C) 2019 Renesas Electronics Corporation
762306a36Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/scatterlist.h>
1262306a36Sopenharmony_ci#include "common.h"
1362306a36Sopenharmony_ci#include "pipe.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define usbhsf_get_cfifo(p)	(&((p)->fifo_info.cfifo))
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define usbhsf_fifo_is_busy(f)	((f)->pipe) /* see usbhs_pipe_select_fifo */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci *		packet initialize
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_civoid usbhs_pkt_init(struct usbhs_pkt *pkt)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	INIT_LIST_HEAD(&pkt->node);
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci *		packet control function
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistatic int usbhsf_null_handle(struct usbhs_pkt *pkt, int *is_done)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
3362306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	dev_err(dev, "null handler\n");
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return -EINVAL;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic const struct usbhs_pkt_handle usbhsf_null_handler = {
4162306a36Sopenharmony_ci	.prepare = usbhsf_null_handle,
4262306a36Sopenharmony_ci	.try_run = usbhsf_null_handle,
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_civoid usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
4662306a36Sopenharmony_ci		    void (*done)(struct usbhs_priv *priv,
4762306a36Sopenharmony_ci				 struct usbhs_pkt *pkt),
4862306a36Sopenharmony_ci		    void *buf, int len, int zero, int sequence)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
5162306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
5262306a36Sopenharmony_ci	unsigned long flags;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (!done) {
5562306a36Sopenharmony_ci		dev_err(dev, "no done function\n");
5662306a36Sopenharmony_ci		return;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/********************  spin lock ********************/
6062306a36Sopenharmony_ci	usbhs_lock(priv, flags);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (!pipe->handler) {
6362306a36Sopenharmony_ci		dev_err(dev, "no handler function\n");
6462306a36Sopenharmony_ci		pipe->handler = &usbhsf_null_handler;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	list_move_tail(&pkt->node, &pipe->list);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/*
7062306a36Sopenharmony_ci	 * each pkt must hold own handler.
7162306a36Sopenharmony_ci	 * because handler might be changed by its situation.
7262306a36Sopenharmony_ci	 * dma handler -> pio handler.
7362306a36Sopenharmony_ci	 */
7462306a36Sopenharmony_ci	pkt->pipe	= pipe;
7562306a36Sopenharmony_ci	pkt->buf	= buf;
7662306a36Sopenharmony_ci	pkt->handler	= pipe->handler;
7762306a36Sopenharmony_ci	pkt->length	= len;
7862306a36Sopenharmony_ci	pkt->zero	= zero;
7962306a36Sopenharmony_ci	pkt->actual	= 0;
8062306a36Sopenharmony_ci	pkt->done	= done;
8162306a36Sopenharmony_ci	pkt->sequence	= sequence;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
8462306a36Sopenharmony_ci	/********************  spin unlock ******************/
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void __usbhsf_pkt_del(struct usbhs_pkt *pkt)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	list_del_init(&pkt->node);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistruct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
9862306a36Sopenharmony_ci				 struct usbhs_fifo *fifo);
9962306a36Sopenharmony_cistatic struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
10062306a36Sopenharmony_ci					    struct usbhs_pkt *pkt);
10162306a36Sopenharmony_ci#define usbhsf_dma_map(p)	__usbhsf_dma_map_ctrl(p, 1)
10262306a36Sopenharmony_ci#define usbhsf_dma_unmap(p)	__usbhsf_dma_map_ctrl(p, 0)
10362306a36Sopenharmony_cistatic int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map);
10462306a36Sopenharmony_cistatic void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable);
10562306a36Sopenharmony_cistatic void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable);
10662306a36Sopenharmony_cistruct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
10962306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
11062306a36Sopenharmony_ci	unsigned long flags;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/********************  spin lock ********************/
11362306a36Sopenharmony_ci	usbhs_lock(priv, flags);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	usbhs_pipe_disable(pipe);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (!pkt)
11862306a36Sopenharmony_ci		pkt = __usbhsf_pkt_get(pipe);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (pkt) {
12162306a36Sopenharmony_ci		struct dma_chan *chan = NULL;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		if (fifo)
12462306a36Sopenharmony_ci			chan = usbhsf_dma_chan_get(fifo, pkt);
12562306a36Sopenharmony_ci		if (chan) {
12662306a36Sopenharmony_ci			dmaengine_terminate_all(chan);
12762306a36Sopenharmony_ci			usbhsf_dma_unmap(pkt);
12862306a36Sopenharmony_ci		} else {
12962306a36Sopenharmony_ci			if (usbhs_pipe_is_dir_in(pipe))
13062306a36Sopenharmony_ci				usbhsf_rx_irq_ctrl(pipe, 0);
13162306a36Sopenharmony_ci			else
13262306a36Sopenharmony_ci				usbhsf_tx_irq_ctrl(pipe, 0);
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		usbhs_pipe_clear_without_sequence(pipe, 0, 0);
13662306a36Sopenharmony_ci		usbhs_pipe_running(pipe, 0);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		__usbhsf_pkt_del(pkt);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (fifo)
14262306a36Sopenharmony_ci		usbhsf_fifo_unselect(pipe, fifo);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
14562306a36Sopenharmony_ci	/********************  spin unlock ******************/
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return pkt;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cienum {
15162306a36Sopenharmony_ci	USBHSF_PKT_PREPARE,
15262306a36Sopenharmony_ci	USBHSF_PKT_TRY_RUN,
15362306a36Sopenharmony_ci	USBHSF_PKT_DMA_DONE,
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
15962306a36Sopenharmony_ci	struct usbhs_pkt *pkt;
16062306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
16162306a36Sopenharmony_ci	int (*func)(struct usbhs_pkt *pkt, int *is_done);
16262306a36Sopenharmony_ci	unsigned long flags;
16362306a36Sopenharmony_ci	int ret = 0;
16462306a36Sopenharmony_ci	int is_done = 0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/********************  spin lock ********************/
16762306a36Sopenharmony_ci	usbhs_lock(priv, flags);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	pkt = __usbhsf_pkt_get(pipe);
17062306a36Sopenharmony_ci	if (!pkt) {
17162306a36Sopenharmony_ci		ret = -EINVAL;
17262306a36Sopenharmony_ci		goto __usbhs_pkt_handler_end;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	switch (type) {
17662306a36Sopenharmony_ci	case USBHSF_PKT_PREPARE:
17762306a36Sopenharmony_ci		func = pkt->handler->prepare;
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci	case USBHSF_PKT_TRY_RUN:
18062306a36Sopenharmony_ci		func = pkt->handler->try_run;
18162306a36Sopenharmony_ci		break;
18262306a36Sopenharmony_ci	case USBHSF_PKT_DMA_DONE:
18362306a36Sopenharmony_ci		func = pkt->handler->dma_done;
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	default:
18662306a36Sopenharmony_ci		dev_err(dev, "unknown pkt handler\n");
18762306a36Sopenharmony_ci		goto __usbhs_pkt_handler_end;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (likely(func))
19162306a36Sopenharmony_ci		ret = func(pkt, &is_done);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (is_done)
19462306a36Sopenharmony_ci		__usbhsf_pkt_del(pkt);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci__usbhs_pkt_handler_end:
19762306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
19862306a36Sopenharmony_ci	/********************  spin unlock ******************/
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (is_done) {
20162306a36Sopenharmony_ci		pkt->done(priv, pkt);
20262306a36Sopenharmony_ci		usbhs_pkt_start(pipe);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return ret;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_civoid usbhs_pkt_start(struct usbhs_pipe *pipe)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	usbhsf_pkt_handler(pipe, USBHSF_PKT_PREPARE);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci *		irq enable/disable function
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_ci#define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, irq_bempsts, e)
21762306a36Sopenharmony_ci#define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, irq_brdysts, e)
21862306a36Sopenharmony_ci#define usbhsf_irq_callback_ctrl(pipe, status, enable)			\
21962306a36Sopenharmony_ci	({								\
22062306a36Sopenharmony_ci		struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);	\
22162306a36Sopenharmony_ci		struct usbhs_mod *mod = usbhs_mod_get_current(priv);	\
22262306a36Sopenharmony_ci		u16 status = (1 << usbhs_pipe_number(pipe));		\
22362306a36Sopenharmony_ci		if (!mod)						\
22462306a36Sopenharmony_ci			return;						\
22562306a36Sopenharmony_ci		if (enable)						\
22662306a36Sopenharmony_ci			mod->status |= status;				\
22762306a36Sopenharmony_ci		else							\
22862306a36Sopenharmony_ci			mod->status &= ~status;				\
22962306a36Sopenharmony_ci		usbhs_irq_callback_update(priv, mod);			\
23062306a36Sopenharmony_ci	})
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * And DCP pipe can NOT use "ready interrupt" for "send"
23662306a36Sopenharmony_ci	 * it should use "empty" interrupt.
23762306a36Sopenharmony_ci	 * see
23862306a36Sopenharmony_ci	 *   "Operation" - "Interrupt Function" - "BRDY Interrupt"
23962306a36Sopenharmony_ci	 *
24062306a36Sopenharmony_ci	 * on the other hand, normal pipe can use "ready interrupt" for "send"
24162306a36Sopenharmony_ci	 * even though it is single/double buffer
24262306a36Sopenharmony_ci	 */
24362306a36Sopenharmony_ci	if (usbhs_pipe_is_dcp(pipe))
24462306a36Sopenharmony_ci		usbhsf_irq_empty_ctrl(pipe, enable);
24562306a36Sopenharmony_ci	else
24662306a36Sopenharmony_ci		usbhsf_irq_ready_ctrl(pipe, enable);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	usbhsf_irq_ready_ctrl(pipe, enable);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/*
25562306a36Sopenharmony_ci *		FIFO ctrl
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_cistatic void usbhsf_send_terminator(struct usbhs_pipe *pipe,
25862306a36Sopenharmony_ci				   struct usbhs_fifo *fifo)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	usbhs_bset(priv, fifo->ctr, BVAL, BVAL);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int usbhsf_fifo_barrier(struct usbhs_priv *priv,
26662306a36Sopenharmony_ci			       struct usbhs_fifo *fifo)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	/* The FIFO port is accessible */
26962306a36Sopenharmony_ci	if (usbhs_read(priv, fifo->ctr) & FRDY)
27062306a36Sopenharmony_ci		return 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return -EBUSY;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
27662306a36Sopenharmony_ci			      struct usbhs_fifo *fifo)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
27962306a36Sopenharmony_ci	int ret = 0;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (!usbhs_pipe_is_dcp(pipe)) {
28262306a36Sopenharmony_ci		/*
28362306a36Sopenharmony_ci		 * This driver checks the pipe condition first to avoid -EBUSY
28462306a36Sopenharmony_ci		 * from usbhsf_fifo_barrier() if the pipe is RX direction and
28562306a36Sopenharmony_ci		 * empty.
28662306a36Sopenharmony_ci		 */
28762306a36Sopenharmony_ci		if (usbhs_pipe_is_dir_in(pipe))
28862306a36Sopenharmony_ci			ret = usbhs_pipe_is_accessible(pipe);
28962306a36Sopenharmony_ci		if (!ret)
29062306a36Sopenharmony_ci			ret = usbhsf_fifo_barrier(priv, fifo);
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/*
29462306a36Sopenharmony_ci	 * if non-DCP pipe, this driver should set BCLR when
29562306a36Sopenharmony_ci	 * usbhsf_fifo_barrier() returns 0.
29662306a36Sopenharmony_ci	 */
29762306a36Sopenharmony_ci	if (!ret)
29862306a36Sopenharmony_ci		usbhs_write(priv, fifo->ctr, BCLR);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int usbhsf_fifo_rcv_len(struct usbhs_priv *priv,
30262306a36Sopenharmony_ci			       struct usbhs_fifo *fifo)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	return usbhs_read(priv, fifo->ctr) & DTLN_MASK;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
30862306a36Sopenharmony_ci				 struct usbhs_fifo *fifo)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	usbhs_pipe_select_fifo(pipe, NULL);
31362306a36Sopenharmony_ci	usbhs_write(priv, fifo->sel, 0);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic int usbhsf_fifo_select(struct usbhs_pipe *pipe,
31762306a36Sopenharmony_ci			      struct usbhs_fifo *fifo,
31862306a36Sopenharmony_ci			      int write)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
32162306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
32262306a36Sopenharmony_ci	int timeout = 1024;
32362306a36Sopenharmony_ci	u16 mask = ((1 << 5) | 0xF);		/* mask of ISEL | CURPIPE */
32462306a36Sopenharmony_ci	u16 base = usbhs_pipe_number(pipe);	/* CURPIPE */
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (usbhs_pipe_is_busy(pipe) ||
32762306a36Sopenharmony_ci	    usbhsf_fifo_is_busy(fifo))
32862306a36Sopenharmony_ci		return -EBUSY;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (usbhs_pipe_is_dcp(pipe)) {
33162306a36Sopenharmony_ci		base |= (1 == write) << 5;	/* ISEL */
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		if (usbhs_mod_is_host(priv))
33462306a36Sopenharmony_ci			usbhs_dcp_dir_for_host(pipe, write);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* "base" will be used below  */
33862306a36Sopenharmony_ci	usbhs_write(priv, fifo->sel, base | MBW_32);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* check ISEL and CURPIPE value */
34162306a36Sopenharmony_ci	while (timeout--) {
34262306a36Sopenharmony_ci		if (base == (mask & usbhs_read(priv, fifo->sel))) {
34362306a36Sopenharmony_ci			usbhs_pipe_select_fifo(pipe, fifo);
34462306a36Sopenharmony_ci			return 0;
34562306a36Sopenharmony_ci		}
34662306a36Sopenharmony_ci		udelay(10);
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	dev_err(dev, "fifo select error\n");
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return -EIO;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/*
35562306a36Sopenharmony_ci *		DCP status stage
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic int usbhs_dcp_dir_switch_to_write(struct usbhs_pkt *pkt, int *is_done)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
36062306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
36162306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
36262306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
36362306a36Sopenharmony_ci	int ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	usbhs_pipe_disable(pipe);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ret = usbhsf_fifo_select(pipe, fifo, 1);
36862306a36Sopenharmony_ci	if (ret < 0) {
36962306a36Sopenharmony_ci		dev_err(dev, "%s() failed\n", __func__);
37062306a36Sopenharmony_ci		return ret;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	usbhsf_fifo_clear(pipe, fifo);
37662306a36Sopenharmony_ci	usbhsf_send_terminator(pipe, fifo);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	usbhsf_tx_irq_ctrl(pipe, 1);
38162306a36Sopenharmony_ci	usbhs_pipe_enable(pipe);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return ret;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic int usbhs_dcp_dir_switch_to_read(struct usbhs_pkt *pkt, int *is_done)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
38962306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
39062306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
39162306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
39262306a36Sopenharmony_ci	int ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	usbhs_pipe_disable(pipe);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	ret = usbhsf_fifo_select(pipe, fifo, 0);
39762306a36Sopenharmony_ci	if (ret < 0) {
39862306a36Sopenharmony_ci		dev_err(dev, "%s() fail\n", __func__);
39962306a36Sopenharmony_ci		return ret;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
40362306a36Sopenharmony_ci	usbhsf_fifo_clear(pipe, fifo);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	usbhsf_rx_irq_ctrl(pipe, 1);
40862306a36Sopenharmony_ci	usbhs_pipe_enable(pipe);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return ret;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int usbhs_dcp_dir_switch_done(struct usbhs_pkt *pkt, int *is_done)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (pkt->handler == &usbhs_dcp_status_stage_in_handler)
41962306a36Sopenharmony_ci		usbhsf_tx_irq_ctrl(pipe, 0);
42062306a36Sopenharmony_ci	else
42162306a36Sopenharmony_ci		usbhsf_rx_irq_ctrl(pipe, 0);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	pkt->actual = pkt->length;
42462306a36Sopenharmony_ci	*is_done = 1;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return 0;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = {
43062306a36Sopenharmony_ci	.prepare = usbhs_dcp_dir_switch_to_write,
43162306a36Sopenharmony_ci	.try_run = usbhs_dcp_dir_switch_done,
43262306a36Sopenharmony_ci};
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = {
43562306a36Sopenharmony_ci	.prepare = usbhs_dcp_dir_switch_to_read,
43662306a36Sopenharmony_ci	.try_run = usbhs_dcp_dir_switch_done,
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/*
44062306a36Sopenharmony_ci *		DCP data stage (push)
44162306a36Sopenharmony_ci */
44262306a36Sopenharmony_cistatic int usbhsf_dcp_data_stage_try_push(struct usbhs_pkt *pkt, int *is_done)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*
44962306a36Sopenharmony_ci	 * change handler to PIO push
45062306a36Sopenharmony_ci	 */
45162306a36Sopenharmony_ci	pkt->handler = &usbhs_fifo_pio_push_handler;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return pkt->handler->prepare(pkt, is_done);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = {
45762306a36Sopenharmony_ci	.prepare = usbhsf_dcp_data_stage_try_push,
45862306a36Sopenharmony_ci};
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/*
46162306a36Sopenharmony_ci *		DCP data stage (pop)
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_cistatic int usbhsf_dcp_data_stage_prepare_pop(struct usbhs_pkt *pkt,
46462306a36Sopenharmony_ci					     int *is_done)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
46762306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
46862306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (usbhs_pipe_is_busy(pipe))
47162306a36Sopenharmony_ci		return 0;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/*
47462306a36Sopenharmony_ci	 * prepare pop for DCP should
47562306a36Sopenharmony_ci	 *  - change DCP direction,
47662306a36Sopenharmony_ci	 *  - clear fifo
47762306a36Sopenharmony_ci	 *  - DATA1
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	usbhs_pipe_disable(pipe);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	usbhsf_fifo_select(pipe, fifo, 0);
48462306a36Sopenharmony_ci	usbhsf_fifo_clear(pipe, fifo);
48562306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/*
48862306a36Sopenharmony_ci	 * change handler to PIO pop
48962306a36Sopenharmony_ci	 */
49062306a36Sopenharmony_ci	pkt->handler = &usbhs_fifo_pio_pop_handler;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return pkt->handler->prepare(pkt, is_done);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = {
49662306a36Sopenharmony_ci	.prepare = usbhsf_dcp_data_stage_prepare_pop,
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci/*
50062306a36Sopenharmony_ci *		PIO push handler
50162306a36Sopenharmony_ci */
50262306a36Sopenharmony_cistatic int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
50562306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
50662306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
50762306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
50862306a36Sopenharmony_ci	void __iomem *addr = priv->base + fifo->port;
50962306a36Sopenharmony_ci	u8 *buf;
51062306a36Sopenharmony_ci	int maxp = usbhs_pipe_get_maxpacket(pipe);
51162306a36Sopenharmony_ci	int total_len;
51262306a36Sopenharmony_ci	int i, ret, len;
51362306a36Sopenharmony_ci	int is_short;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	usbhs_pipe_data_sequence(pipe, pkt->sequence);
51662306a36Sopenharmony_ci	pkt->sequence = -1; /* -1 sequence will be ignored */
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	ret = usbhsf_fifo_select(pipe, fifo, 1);
52162306a36Sopenharmony_ci	if (ret < 0)
52262306a36Sopenharmony_ci		return 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	ret = usbhs_pipe_is_accessible(pipe);
52562306a36Sopenharmony_ci	if (ret < 0) {
52662306a36Sopenharmony_ci		/* inaccessible pipe is not an error */
52762306a36Sopenharmony_ci		ret = 0;
52862306a36Sopenharmony_ci		goto usbhs_fifo_write_busy;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	ret = usbhsf_fifo_barrier(priv, fifo);
53262306a36Sopenharmony_ci	if (ret < 0)
53362306a36Sopenharmony_ci		goto usbhs_fifo_write_busy;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	buf		= pkt->buf    + pkt->actual;
53662306a36Sopenharmony_ci	len		= pkt->length - pkt->actual;
53762306a36Sopenharmony_ci	len		= min(len, maxp);
53862306a36Sopenharmony_ci	total_len	= len;
53962306a36Sopenharmony_ci	is_short	= total_len < maxp;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/*
54262306a36Sopenharmony_ci	 * FIXME
54362306a36Sopenharmony_ci	 *
54462306a36Sopenharmony_ci	 * 32-bit access only
54562306a36Sopenharmony_ci	 */
54662306a36Sopenharmony_ci	if (len >= 4 && !((unsigned long)buf & 0x03)) {
54762306a36Sopenharmony_ci		iowrite32_rep(addr, buf, len / 4);
54862306a36Sopenharmony_ci		len %= 4;
54962306a36Sopenharmony_ci		buf += total_len - len;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* the rest operation */
55362306a36Sopenharmony_ci	if (usbhs_get_dparam(priv, cfifo_byte_addr)) {
55462306a36Sopenharmony_ci		for (i = 0; i < len; i++)
55562306a36Sopenharmony_ci			iowrite8(buf[i], addr + (i & 0x03));
55662306a36Sopenharmony_ci	} else {
55762306a36Sopenharmony_ci		for (i = 0; i < len; i++)
55862306a36Sopenharmony_ci			iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/*
56262306a36Sopenharmony_ci	 * variable update
56362306a36Sopenharmony_ci	 */
56462306a36Sopenharmony_ci	pkt->actual += total_len;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (pkt->actual < pkt->length)
56762306a36Sopenharmony_ci		*is_done = 0;		/* there are remainder data */
56862306a36Sopenharmony_ci	else if (is_short)
56962306a36Sopenharmony_ci		*is_done = 1;		/* short packet */
57062306a36Sopenharmony_ci	else
57162306a36Sopenharmony_ci		*is_done = !pkt->zero;	/* send zero packet ? */
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/*
57462306a36Sopenharmony_ci	 * pipe/irq handling
57562306a36Sopenharmony_ci	 */
57662306a36Sopenharmony_ci	if (is_short)
57762306a36Sopenharmony_ci		usbhsf_send_terminator(pipe, fifo);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	usbhsf_tx_irq_ctrl(pipe, !*is_done);
58062306a36Sopenharmony_ci	usbhs_pipe_running(pipe, !*is_done);
58162306a36Sopenharmony_ci	usbhs_pipe_enable(pipe);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	dev_dbg(dev, "  send %d (%d/ %d/ %d/ %d)\n",
58462306a36Sopenharmony_ci		usbhs_pipe_number(pipe),
58562306a36Sopenharmony_ci		pkt->length, pkt->actual, *is_done, pkt->zero);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciusbhs_fifo_write_busy:
59262306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/*
59562306a36Sopenharmony_ci	 * pipe is busy.
59662306a36Sopenharmony_ci	 * retry in interrupt
59762306a36Sopenharmony_ci	 */
59862306a36Sopenharmony_ci	usbhsf_tx_irq_ctrl(pipe, 1);
59962306a36Sopenharmony_ci	usbhs_pipe_running(pipe, 1);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return ret;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	if (usbhs_pipe_is_running(pkt->pipe))
60762306a36Sopenharmony_ci		return 0;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return usbhsf_pio_try_push(pkt, is_done);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
61362306a36Sopenharmony_ci	.prepare = usbhsf_pio_prepare_push,
61462306a36Sopenharmony_ci	.try_run = usbhsf_pio_try_push,
61562306a36Sopenharmony_ci};
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/*
61862306a36Sopenharmony_ci *		PIO pop handler
61962306a36Sopenharmony_ci */
62062306a36Sopenharmony_cistatic int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
62362306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
62462306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (usbhs_pipe_is_busy(pipe))
62762306a36Sopenharmony_ci		return 0;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	if (usbhs_pipe_is_running(pipe))
63062306a36Sopenharmony_ci		return 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/*
63362306a36Sopenharmony_ci	 * pipe enable to prepare packet receive
63462306a36Sopenharmony_ci	 */
63562306a36Sopenharmony_ci	usbhs_pipe_data_sequence(pipe, pkt->sequence);
63662306a36Sopenharmony_ci	pkt->sequence = -1; /* -1 sequence will be ignored */
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (usbhs_pipe_is_dcp(pipe))
63962306a36Sopenharmony_ci		usbhsf_fifo_clear(pipe, fifo);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
64262306a36Sopenharmony_ci	usbhs_pipe_enable(pipe);
64362306a36Sopenharmony_ci	usbhs_pipe_running(pipe, 1);
64462306a36Sopenharmony_ci	usbhsf_rx_irq_ctrl(pipe, 1);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return 0;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
65262306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
65362306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
65462306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
65562306a36Sopenharmony_ci	void __iomem *addr = priv->base + fifo->port;
65662306a36Sopenharmony_ci	u8 *buf;
65762306a36Sopenharmony_ci	u32 data = 0;
65862306a36Sopenharmony_ci	int maxp = usbhs_pipe_get_maxpacket(pipe);
65962306a36Sopenharmony_ci	int rcv_len, len;
66062306a36Sopenharmony_ci	int i, ret;
66162306a36Sopenharmony_ci	int total_len = 0;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	ret = usbhsf_fifo_select(pipe, fifo, 0);
66462306a36Sopenharmony_ci	if (ret < 0)
66562306a36Sopenharmony_ci		return 0;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	ret = usbhsf_fifo_barrier(priv, fifo);
66862306a36Sopenharmony_ci	if (ret < 0)
66962306a36Sopenharmony_ci		goto usbhs_fifo_read_busy;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	buf		= pkt->buf    + pkt->actual;
67462306a36Sopenharmony_ci	len		= pkt->length - pkt->actual;
67562306a36Sopenharmony_ci	len		= min(len, rcv_len);
67662306a36Sopenharmony_ci	total_len	= len;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/*
67962306a36Sopenharmony_ci	 * update actual length first here to decide disable pipe.
68062306a36Sopenharmony_ci	 * if this pipe keeps BUF status and all data were popped,
68162306a36Sopenharmony_ci	 * then, next interrupt/token will be issued again
68262306a36Sopenharmony_ci	 */
68362306a36Sopenharmony_ci	pkt->actual += total_len;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if ((pkt->actual == pkt->length) ||	/* receive all data */
68662306a36Sopenharmony_ci	    (total_len < maxp)) {		/* short packet */
68762306a36Sopenharmony_ci		*is_done = 1;
68862306a36Sopenharmony_ci		usbhsf_rx_irq_ctrl(pipe, 0);
68962306a36Sopenharmony_ci		usbhs_pipe_running(pipe, 0);
69062306a36Sopenharmony_ci		/*
69162306a36Sopenharmony_ci		 * If function mode, since this controller is possible to enter
69262306a36Sopenharmony_ci		 * Control Write status stage at this timing, this driver
69362306a36Sopenharmony_ci		 * should not disable the pipe. If such a case happens, this
69462306a36Sopenharmony_ci		 * controller is not able to complete the status stage.
69562306a36Sopenharmony_ci		 */
69662306a36Sopenharmony_ci		if (!usbhs_mod_is_host(priv) && !usbhs_pipe_is_dcp(pipe))
69762306a36Sopenharmony_ci			usbhs_pipe_disable(pipe);	/* disable pipe first */
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/*
70162306a36Sopenharmony_ci	 * Buffer clear if Zero-Length packet
70262306a36Sopenharmony_ci	 *
70362306a36Sopenharmony_ci	 * see
70462306a36Sopenharmony_ci	 * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
70562306a36Sopenharmony_ci	 */
70662306a36Sopenharmony_ci	if (0 == rcv_len) {
70762306a36Sopenharmony_ci		pkt->zero = 1;
70862306a36Sopenharmony_ci		usbhsf_fifo_clear(pipe, fifo);
70962306a36Sopenharmony_ci		goto usbhs_fifo_read_end;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/*
71362306a36Sopenharmony_ci	 * FIXME
71462306a36Sopenharmony_ci	 *
71562306a36Sopenharmony_ci	 * 32-bit access only
71662306a36Sopenharmony_ci	 */
71762306a36Sopenharmony_ci	if (len >= 4 && !((unsigned long)buf & 0x03)) {
71862306a36Sopenharmony_ci		ioread32_rep(addr, buf, len / 4);
71962306a36Sopenharmony_ci		len %= 4;
72062306a36Sopenharmony_ci		buf += total_len - len;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* the rest operation */
72462306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
72562306a36Sopenharmony_ci		if (!(i & 0x03))
72662306a36Sopenharmony_ci			data = ioread32(addr);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ciusbhs_fifo_read_end:
73262306a36Sopenharmony_ci	dev_dbg(dev, "  recv %d (%d/ %d/ %d/ %d)\n",
73362306a36Sopenharmony_ci		usbhs_pipe_number(pipe),
73462306a36Sopenharmony_ci		pkt->length, pkt->actual, *is_done, pkt->zero);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ciusbhs_fifo_read_busy:
73762306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	return ret;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler = {
74362306a36Sopenharmony_ci	.prepare = usbhsf_prepare_pop,
74462306a36Sopenharmony_ci	.try_run = usbhsf_pio_try_pop,
74562306a36Sopenharmony_ci};
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/*
74862306a36Sopenharmony_ci *		DCP ctrol statge handler
74962306a36Sopenharmony_ci */
75062306a36Sopenharmony_cistatic int usbhsf_ctrl_stage_end(struct usbhs_pkt *pkt, int *is_done)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	usbhs_dcp_control_transfer_done(pkt->pipe);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	*is_done = 1;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return 0;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler = {
76062306a36Sopenharmony_ci	.prepare = usbhsf_ctrl_stage_end,
76162306a36Sopenharmony_ci	.try_run = usbhsf_ctrl_stage_end,
76262306a36Sopenharmony_ci};
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci/*
76562306a36Sopenharmony_ci *		DMA fifo functions
76662306a36Sopenharmony_ci */
76762306a36Sopenharmony_cistatic struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
76862306a36Sopenharmony_ci					    struct usbhs_pkt *pkt)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	if (&usbhs_fifo_dma_push_handler == pkt->handler)
77162306a36Sopenharmony_ci		return fifo->tx_chan;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (&usbhs_fifo_dma_pop_handler == pkt->handler)
77462306a36Sopenharmony_ci		return fifo->rx_chan;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	return NULL;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic struct usbhs_fifo *usbhsf_get_dma_fifo(struct usbhs_priv *priv,
78062306a36Sopenharmony_ci					      struct usbhs_pkt *pkt)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct usbhs_fifo *fifo;
78362306a36Sopenharmony_ci	int i;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	usbhs_for_each_dfifo(priv, fifo, i) {
78662306a36Sopenharmony_ci		if (usbhsf_dma_chan_get(fifo, pkt) &&
78762306a36Sopenharmony_ci		    !usbhsf_fifo_is_busy(fifo))
78862306a36Sopenharmony_ci			return fifo;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	return NULL;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci#define usbhsf_dma_start(p, f)	__usbhsf_dma_ctrl(p, f, DREQE)
79562306a36Sopenharmony_ci#define usbhsf_dma_stop(p, f)	__usbhsf_dma_ctrl(p, f, 0)
79662306a36Sopenharmony_cistatic void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe,
79762306a36Sopenharmony_ci			      struct usbhs_fifo *fifo,
79862306a36Sopenharmony_ci			      u16 dreqe)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	usbhs_bset(priv, fifo->sel, DREQE, dreqe);
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
80862306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
80962306a36Sopenharmony_ci	struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
81062306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
81162306a36Sopenharmony_ci	struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return info->dma_map_ctrl(chan->device->dev, pkt, map);
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic void usbhsf_dma_complete(void *arg,
81762306a36Sopenharmony_ci				const struct dmaengine_result *result);
81862306a36Sopenharmony_cistatic void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
82162306a36Sopenharmony_ci	struct usbhs_fifo *fifo;
82262306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
82362306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
82462306a36Sopenharmony_ci	struct dma_chan *chan;
82562306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
82662306a36Sopenharmony_ci	enum dma_transfer_direction dir;
82762306a36Sopenharmony_ci	dma_cookie_t cookie;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	fifo = usbhs_pipe_to_fifo(pipe);
83062306a36Sopenharmony_ci	if (!fifo)
83162306a36Sopenharmony_ci		return;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	chan = usbhsf_dma_chan_get(fifo, pkt);
83462306a36Sopenharmony_ci	dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual,
83762306a36Sopenharmony_ci					pkt->trans, dir,
83862306a36Sopenharmony_ci					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
83962306a36Sopenharmony_ci	if (!desc)
84062306a36Sopenharmony_ci		return;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	desc->callback_result	= usbhsf_dma_complete;
84362306a36Sopenharmony_ci	desc->callback_param	= pkt;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	cookie = dmaengine_submit(desc);
84662306a36Sopenharmony_ci	if (cookie < 0) {
84762306a36Sopenharmony_ci		dev_err(dev, "Failed to submit dma descriptor\n");
84862306a36Sopenharmony_ci		return;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	dev_dbg(dev, "  %s %d (%d/ %d)\n",
85262306a36Sopenharmony_ci		fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	usbhs_pipe_running(pipe, 1);
85562306a36Sopenharmony_ci	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
85662306a36Sopenharmony_ci	dma_async_issue_pending(chan);
85762306a36Sopenharmony_ci	usbhsf_dma_start(pipe, fifo);
85862306a36Sopenharmony_ci	usbhs_pipe_enable(pipe);
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic void xfer_work(struct work_struct *work)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
86462306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
86562306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
86662306a36Sopenharmony_ci	unsigned long flags;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	usbhs_lock(priv, flags);
86962306a36Sopenharmony_ci	usbhsf_dma_xfer_preparing(pkt);
87062306a36Sopenharmony_ci	usbhs_unlock(priv, flags);
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci/*
87462306a36Sopenharmony_ci *		DMA push handler
87562306a36Sopenharmony_ci */
87662306a36Sopenharmony_cistatic int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
87962306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
88062306a36Sopenharmony_ci	struct usbhs_fifo *fifo;
88162306a36Sopenharmony_ci	int len = pkt->length - pkt->actual;
88262306a36Sopenharmony_ci	int ret;
88362306a36Sopenharmony_ci	uintptr_t align_mask;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (usbhs_pipe_is_busy(pipe))
88662306a36Sopenharmony_ci		return 0;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/* use PIO if packet is less than pio_dma_border or pipe is DCP */
88962306a36Sopenharmony_ci	if ((len < usbhs_get_dparam(priv, pio_dma_border)) ||
89062306a36Sopenharmony_ci	    usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
89162306a36Sopenharmony_ci		goto usbhsf_pio_prepare_push;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	/* check data length if this driver don't use USB-DMAC */
89462306a36Sopenharmony_ci	if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7)
89562306a36Sopenharmony_ci		goto usbhsf_pio_prepare_push;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* check buffer alignment */
89862306a36Sopenharmony_ci	align_mask = usbhs_get_dparam(priv, has_usb_dmac) ?
89962306a36Sopenharmony_ci					USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7;
90062306a36Sopenharmony_ci	if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask)
90162306a36Sopenharmony_ci		goto usbhsf_pio_prepare_push;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* return at this time if the pipe is running */
90462306a36Sopenharmony_ci	if (usbhs_pipe_is_running(pipe))
90562306a36Sopenharmony_ci		return 0;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* get enable DMA fifo */
90862306a36Sopenharmony_ci	fifo = usbhsf_get_dma_fifo(priv, pkt);
90962306a36Sopenharmony_ci	if (!fifo)
91062306a36Sopenharmony_ci		goto usbhsf_pio_prepare_push;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	ret = usbhsf_fifo_select(pipe, fifo, 0);
91362306a36Sopenharmony_ci	if (ret < 0)
91462306a36Sopenharmony_ci		goto usbhsf_pio_prepare_push;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (usbhsf_dma_map(pkt) < 0)
91762306a36Sopenharmony_ci		goto usbhsf_pio_prepare_push_unselect;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	pkt->trans = len;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	usbhsf_tx_irq_ctrl(pipe, 0);
92262306a36Sopenharmony_ci	/* FIXME: Workaound for usb dmac that driver can be used in atomic */
92362306a36Sopenharmony_ci	if (usbhs_get_dparam(priv, has_usb_dmac)) {
92462306a36Sopenharmony_ci		usbhsf_dma_xfer_preparing(pkt);
92562306a36Sopenharmony_ci	} else {
92662306a36Sopenharmony_ci		INIT_WORK(&pkt->work, xfer_work);
92762306a36Sopenharmony_ci		schedule_work(&pkt->work);
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return 0;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciusbhsf_pio_prepare_push_unselect:
93362306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
93462306a36Sopenharmony_ciusbhsf_pio_prepare_push:
93562306a36Sopenharmony_ci	/*
93662306a36Sopenharmony_ci	 * change handler to PIO
93762306a36Sopenharmony_ci	 */
93862306a36Sopenharmony_ci	pkt->handler = &usbhs_fifo_pio_push_handler;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return pkt->handler->prepare(pkt, is_done);
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
94662306a36Sopenharmony_ci	int is_short = pkt->trans % usbhs_pipe_get_maxpacket(pipe);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	pkt->actual += pkt->trans;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (pkt->actual < pkt->length)
95162306a36Sopenharmony_ci		*is_done = 0;		/* there are remainder data */
95262306a36Sopenharmony_ci	else if (is_short)
95362306a36Sopenharmony_ci		*is_done = 1;		/* short packet */
95462306a36Sopenharmony_ci	else
95562306a36Sopenharmony_ci		*is_done = !pkt->zero;	/* send zero packet? */
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	usbhs_pipe_running(pipe, !*is_done);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	usbhsf_dma_stop(pipe, pipe->fifo);
96062306a36Sopenharmony_ci	usbhsf_dma_unmap(pkt);
96162306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, pipe->fifo);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (!*is_done) {
96462306a36Sopenharmony_ci		/* change handler to PIO */
96562306a36Sopenharmony_ci		pkt->handler = &usbhs_fifo_pio_push_handler;
96662306a36Sopenharmony_ci		return pkt->handler->try_run(pkt, is_done);
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return 0;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
97362306a36Sopenharmony_ci	.prepare	= usbhsf_dma_prepare_push,
97462306a36Sopenharmony_ci	.dma_done	= usbhsf_dma_push_done,
97562306a36Sopenharmony_ci};
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci/*
97862306a36Sopenharmony_ci *		DMA pop handler
97962306a36Sopenharmony_ci */
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt,
98262306a36Sopenharmony_ci					      int *is_done)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	return usbhsf_prepare_pop(pkt, is_done);
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt,
98862306a36Sopenharmony_ci						int *is_done)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
99162306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
99262306a36Sopenharmony_ci	struct usbhs_fifo *fifo;
99362306a36Sopenharmony_ci	int ret;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (usbhs_pipe_is_busy(pipe))
99662306a36Sopenharmony_ci		return 0;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* use PIO if packet is less than pio_dma_border or pipe is DCP */
99962306a36Sopenharmony_ci	if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) ||
100062306a36Sopenharmony_ci	    usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
100162306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	fifo = usbhsf_get_dma_fifo(priv, pkt);
100462306a36Sopenharmony_ci	if (!fifo)
100562306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
100862306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* return at this time if the pipe is running */
101162306a36Sopenharmony_ci	if (usbhs_pipe_is_running(pipe))
101262306a36Sopenharmony_ci		return 0;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	usbhs_pipe_config_change_bfre(pipe, 1);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	ret = usbhsf_fifo_select(pipe, fifo, 0);
101762306a36Sopenharmony_ci	if (ret < 0)
101862306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	if (usbhsf_dma_map(pkt) < 0)
102162306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop_unselect;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* DMA */
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/*
102662306a36Sopenharmony_ci	 * usbhs_fifo_dma_pop_handler :: prepare
102762306a36Sopenharmony_ci	 * enabled irq to come here.
102862306a36Sopenharmony_ci	 * but it is no longer needed for DMA. disable it.
102962306a36Sopenharmony_ci	 */
103062306a36Sopenharmony_ci	usbhsf_rx_irq_ctrl(pipe, 0);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	pkt->trans = pkt->length;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	usbhsf_dma_xfer_preparing(pkt);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	return 0;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ciusbhsf_pio_prepare_pop_unselect:
103962306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
104062306a36Sopenharmony_ciusbhsf_pio_prepare_pop:
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/*
104362306a36Sopenharmony_ci	 * change handler to PIO
104462306a36Sopenharmony_ci	 */
104562306a36Sopenharmony_ci	pkt->handler = &usbhs_fifo_pio_pop_handler;
104662306a36Sopenharmony_ci	usbhs_pipe_config_change_bfre(pipe, 0);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	return pkt->handler->prepare(pkt, is_done);
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	if (usbhs_get_dparam(priv, has_usb_dmac))
105662306a36Sopenharmony_ci		return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done);
105762306a36Sopenharmony_ci	else
105862306a36Sopenharmony_ci		return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done);
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
106462306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
106562306a36Sopenharmony_ci	struct usbhs_fifo *fifo;
106662306a36Sopenharmony_ci	int len, ret;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (usbhs_pipe_is_busy(pipe))
106962306a36Sopenharmony_ci		return 0;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	if (usbhs_pipe_is_dcp(pipe))
107262306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	/* get enable DMA fifo */
107562306a36Sopenharmony_ci	fifo = usbhsf_get_dma_fifo(priv, pkt);
107662306a36Sopenharmony_ci	if (!fifo)
107762306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
108062306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	ret = usbhsf_fifo_select(pipe, fifo, 0);
108362306a36Sopenharmony_ci	if (ret < 0)
108462306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* use PIO if packet is less than pio_dma_border */
108762306a36Sopenharmony_ci	len = usbhsf_fifo_rcv_len(priv, fifo);
108862306a36Sopenharmony_ci	len = min(pkt->length - pkt->actual, len);
108962306a36Sopenharmony_ci	if (len & 0x7) /* 8byte alignment */
109062306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop_unselect;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	if (len < usbhs_get_dparam(priv, pio_dma_border))
109362306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop_unselect;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	ret = usbhsf_fifo_barrier(priv, fifo);
109662306a36Sopenharmony_ci	if (ret < 0)
109762306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop_unselect;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	if (usbhsf_dma_map(pkt) < 0)
110062306a36Sopenharmony_ci		goto usbhsf_pio_prepare_pop_unselect;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	/* DMA */
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/*
110562306a36Sopenharmony_ci	 * usbhs_fifo_dma_pop_handler :: prepare
110662306a36Sopenharmony_ci	 * enabled irq to come here.
110762306a36Sopenharmony_ci	 * but it is no longer needed for DMA. disable it.
110862306a36Sopenharmony_ci	 */
110962306a36Sopenharmony_ci	usbhsf_rx_irq_ctrl(pipe, 0);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	pkt->trans = len;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	INIT_WORK(&pkt->work, xfer_work);
111462306a36Sopenharmony_ci	schedule_work(&pkt->work);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	return 0;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ciusbhsf_pio_prepare_pop_unselect:
111962306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
112062306a36Sopenharmony_ciusbhsf_pio_prepare_pop:
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	/*
112362306a36Sopenharmony_ci	 * change handler to PIO
112462306a36Sopenharmony_ci	 */
112562306a36Sopenharmony_ci	pkt->handler = &usbhs_fifo_pio_pop_handler;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	return pkt->handler->try_run(pkt, is_done);
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	BUG_ON(usbhs_get_dparam(priv, has_usb_dmac));
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done);
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
114262306a36Sopenharmony_ci	int maxp = usbhs_pipe_get_maxpacket(pipe);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	usbhsf_dma_stop(pipe, pipe->fifo);
114562306a36Sopenharmony_ci	usbhsf_dma_unmap(pkt);
114662306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, pipe->fifo);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	pkt->actual += pkt->trans;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if ((pkt->actual == pkt->length) ||	/* receive all data */
115162306a36Sopenharmony_ci	    (pkt->trans < maxp)) {		/* short packet */
115262306a36Sopenharmony_ci		*is_done = 1;
115362306a36Sopenharmony_ci		usbhs_pipe_running(pipe, 0);
115462306a36Sopenharmony_ci	} else {
115562306a36Sopenharmony_ci		/* re-enable */
115662306a36Sopenharmony_ci		usbhs_pipe_running(pipe, 0);
115762306a36Sopenharmony_ci		usbhsf_prepare_pop(pkt, is_done);
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return 0;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt,
116462306a36Sopenharmony_ci					   struct dma_chan *chan, int dtln)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
116762306a36Sopenharmony_ci	size_t received_size;
116862306a36Sopenharmony_ci	int maxp = usbhs_pipe_get_maxpacket(pipe);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	received_size = pkt->length - pkt->dma_result->residue;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (dtln) {
117362306a36Sopenharmony_ci		received_size -= USBHS_USB_DMAC_XFER_SIZE;
117462306a36Sopenharmony_ci		received_size &= ~(maxp - 1);
117562306a36Sopenharmony_ci		received_size += dtln;
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	return received_size;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt,
118262306a36Sopenharmony_ci					     int *is_done)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
118562306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
118662306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
118762306a36Sopenharmony_ci	struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
118862306a36Sopenharmony_ci	int rcv_len;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/*
119162306a36Sopenharmony_ci	 * Since the driver disables rx_irq in DMA mode, the interrupt handler
119262306a36Sopenharmony_ci	 * cannot the BRDYSTS. So, the function clears it here because the
119362306a36Sopenharmony_ci	 * driver may use PIO mode next time.
119462306a36Sopenharmony_ci	 */
119562306a36Sopenharmony_ci	usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe));
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
119862306a36Sopenharmony_ci	usbhsf_fifo_clear(pipe, fifo);
119962306a36Sopenharmony_ci	pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	usbhs_pipe_running(pipe, 0);
120262306a36Sopenharmony_ci	usbhsf_dma_stop(pipe, fifo);
120362306a36Sopenharmony_ci	usbhsf_dma_unmap(pkt);
120462306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, pipe->fifo);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	/* The driver can assume the rx transaction is always "done" */
120762306a36Sopenharmony_ci	*is_done = 1;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	return 0;
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
121362306a36Sopenharmony_ci{
121462306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (usbhs_get_dparam(priv, has_usb_dmac))
121762306a36Sopenharmony_ci		return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done);
121862306a36Sopenharmony_ci	else
121962306a36Sopenharmony_ci		return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done);
122062306a36Sopenharmony_ci}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ciconst struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
122362306a36Sopenharmony_ci	.prepare	= usbhsf_dma_prepare_pop,
122462306a36Sopenharmony_ci	.try_run	= usbhsf_dma_try_pop,
122562306a36Sopenharmony_ci	.dma_done	= usbhsf_dma_pop_done
122662306a36Sopenharmony_ci};
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci/*
122962306a36Sopenharmony_ci *		DMA setting
123062306a36Sopenharmony_ci */
123162306a36Sopenharmony_cistatic bool usbhsf_dma_filter(struct dma_chan *chan, void *param)
123262306a36Sopenharmony_ci{
123362306a36Sopenharmony_ci	struct sh_dmae_slave *slave = param;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/*
123662306a36Sopenharmony_ci	 * FIXME
123762306a36Sopenharmony_ci	 *
123862306a36Sopenharmony_ci	 * usbhs doesn't recognize id = 0 as valid DMA
123962306a36Sopenharmony_ci	 */
124062306a36Sopenharmony_ci	if (0 == slave->shdma_slave.slave_id)
124162306a36Sopenharmony_ci		return false;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	chan->private = slave;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return true;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	if (fifo->tx_chan)
125162306a36Sopenharmony_ci		dma_release_channel(fifo->tx_chan);
125262306a36Sopenharmony_ci	if (fifo->rx_chan)
125362306a36Sopenharmony_ci		dma_release_channel(fifo->rx_chan);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	fifo->tx_chan = NULL;
125662306a36Sopenharmony_ci	fifo->rx_chan = NULL;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	dma_cap_mask_t mask;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	dma_cap_zero(mask);
126462306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mask);
126562306a36Sopenharmony_ci	fifo->tx_chan = dma_request_channel(mask, usbhsf_dma_filter,
126662306a36Sopenharmony_ci					    &fifo->tx_slave);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	dma_cap_zero(mask);
126962306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mask);
127062306a36Sopenharmony_ci	fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
127162306a36Sopenharmony_ci					    &fifo->rx_slave);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
127562306a36Sopenharmony_ci			       int channel)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	char name[16];
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	/*
128062306a36Sopenharmony_ci	 * To avoid complex handing for DnFIFOs, the driver uses each
128162306a36Sopenharmony_ci	 * DnFIFO as TX or RX direction (not bi-direction).
128262306a36Sopenharmony_ci	 * So, the driver uses odd channels for TX, even channels for RX.
128362306a36Sopenharmony_ci	 */
128462306a36Sopenharmony_ci	snprintf(name, sizeof(name), "ch%d", channel);
128562306a36Sopenharmony_ci	if (channel & 1) {
128662306a36Sopenharmony_ci		fifo->tx_chan = dma_request_chan(dev, name);
128762306a36Sopenharmony_ci		if (IS_ERR(fifo->tx_chan))
128862306a36Sopenharmony_ci			fifo->tx_chan = NULL;
128962306a36Sopenharmony_ci	} else {
129062306a36Sopenharmony_ci		fifo->rx_chan = dma_request_chan(dev, name);
129162306a36Sopenharmony_ci		if (IS_ERR(fifo->rx_chan))
129262306a36Sopenharmony_ci			fifo->rx_chan = NULL;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
129762306a36Sopenharmony_ci			    int channel)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (dev_of_node(dev))
130262306a36Sopenharmony_ci		usbhsf_dma_init_dt(dev, fifo, channel);
130362306a36Sopenharmony_ci	else
130462306a36Sopenharmony_ci		usbhsf_dma_init_pdev(fifo);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (fifo->tx_chan || fifo->rx_chan)
130762306a36Sopenharmony_ci		dev_dbg(dev, "enable DMAEngine (%s%s%s)\n",
130862306a36Sopenharmony_ci			 fifo->name,
130962306a36Sopenharmony_ci			 fifo->tx_chan ? "[TX]" : "    ",
131062306a36Sopenharmony_ci			 fifo->rx_chan ? "[RX]" : "    ");
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci/*
131462306a36Sopenharmony_ci *		irq functions
131562306a36Sopenharmony_ci */
131662306a36Sopenharmony_cistatic int usbhsf_irq_empty(struct usbhs_priv *priv,
131762306a36Sopenharmony_ci			    struct usbhs_irq_state *irq_state)
131862306a36Sopenharmony_ci{
131962306a36Sopenharmony_ci	struct usbhs_pipe *pipe;
132062306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
132162306a36Sopenharmony_ci	int i, ret;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (!irq_state->bempsts) {
132462306a36Sopenharmony_ci		dev_err(dev, "debug %s !!\n", __func__);
132562306a36Sopenharmony_ci		return -EIO;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	dev_dbg(dev, "irq empty [0x%04x]\n", irq_state->bempsts);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/*
133162306a36Sopenharmony_ci	 * search interrupted "pipe"
133262306a36Sopenharmony_ci	 * not "uep".
133362306a36Sopenharmony_ci	 */
133462306a36Sopenharmony_ci	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
133562306a36Sopenharmony_ci		if (!(irq_state->bempsts & (1 << i)))
133662306a36Sopenharmony_ci			continue;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci		ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN);
133962306a36Sopenharmony_ci		if (ret < 0)
134062306a36Sopenharmony_ci			dev_err(dev, "irq_empty run_error %d : %d\n", i, ret);
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	return 0;
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic int usbhsf_irq_ready(struct usbhs_priv *priv,
134762306a36Sopenharmony_ci			    struct usbhs_irq_state *irq_state)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct usbhs_pipe *pipe;
135062306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
135162306a36Sopenharmony_ci	int i, ret;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (!irq_state->brdysts) {
135462306a36Sopenharmony_ci		dev_err(dev, "debug %s !!\n", __func__);
135562306a36Sopenharmony_ci		return -EIO;
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	dev_dbg(dev, "irq ready [0x%04x]\n", irq_state->brdysts);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	/*
136162306a36Sopenharmony_ci	 * search interrupted "pipe"
136262306a36Sopenharmony_ci	 * not "uep".
136362306a36Sopenharmony_ci	 */
136462306a36Sopenharmony_ci	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
136562306a36Sopenharmony_ci		if (!(irq_state->brdysts & (1 << i)))
136662306a36Sopenharmony_ci			continue;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN);
136962306a36Sopenharmony_ci		if (ret < 0)
137062306a36Sopenharmony_ci			dev_err(dev, "irq_ready run_error %d : %d\n", i, ret);
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	return 0;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic void usbhsf_dma_complete(void *arg,
137762306a36Sopenharmony_ci				const struct dmaengine_result *result)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	struct usbhs_pkt *pkt = arg;
138062306a36Sopenharmony_ci	struct usbhs_pipe *pipe = pkt->pipe;
138162306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
138262306a36Sopenharmony_ci	struct device *dev = usbhs_priv_to_dev(priv);
138362306a36Sopenharmony_ci	int ret;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	pkt->dma_result = result;
138662306a36Sopenharmony_ci	ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE);
138762306a36Sopenharmony_ci	if (ret < 0)
138862306a36Sopenharmony_ci		dev_err(dev, "dma_complete run_error %d : %d\n",
138962306a36Sopenharmony_ci			usbhs_pipe_number(pipe), ret);
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_civoid usbhs_fifo_clear_dcp(struct usbhs_pipe *pipe)
139362306a36Sopenharmony_ci{
139462306a36Sopenharmony_ci	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
139562306a36Sopenharmony_ci	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/* clear DCP FIFO of transmission */
139862306a36Sopenharmony_ci	if (usbhsf_fifo_select(pipe, fifo, 1) < 0)
139962306a36Sopenharmony_ci		return;
140062306a36Sopenharmony_ci	usbhsf_fifo_clear(pipe, fifo);
140162306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* clear DCP FIFO of reception */
140462306a36Sopenharmony_ci	if (usbhsf_fifo_select(pipe, fifo, 0) < 0)
140562306a36Sopenharmony_ci		return;
140662306a36Sopenharmony_ci	usbhsf_fifo_clear(pipe, fifo);
140762306a36Sopenharmony_ci	usbhsf_fifo_unselect(pipe, fifo);
140862306a36Sopenharmony_ci}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci/*
141162306a36Sopenharmony_ci *		fifo init
141262306a36Sopenharmony_ci */
141362306a36Sopenharmony_civoid usbhs_fifo_init(struct usbhs_priv *priv)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
141662306a36Sopenharmony_ci	struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv);
141762306a36Sopenharmony_ci	struct usbhs_fifo *dfifo;
141862306a36Sopenharmony_ci	int i;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	mod->irq_empty		= usbhsf_irq_empty;
142162306a36Sopenharmony_ci	mod->irq_ready		= usbhsf_irq_ready;
142262306a36Sopenharmony_ci	mod->irq_bempsts	= 0;
142362306a36Sopenharmony_ci	mod->irq_brdysts	= 0;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	cfifo->pipe	= NULL;
142662306a36Sopenharmony_ci	usbhs_for_each_dfifo(priv, dfifo, i)
142762306a36Sopenharmony_ci		dfifo->pipe	= NULL;
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_civoid usbhs_fifo_quit(struct usbhs_priv *priv)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	mod->irq_empty		= NULL;
143562306a36Sopenharmony_ci	mod->irq_ready		= NULL;
143662306a36Sopenharmony_ci	mod->irq_bempsts	= 0;
143762306a36Sopenharmony_ci	mod->irq_brdysts	= 0;
143862306a36Sopenharmony_ci}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci#define __USBHS_DFIFO_INIT(priv, fifo, channel, fifo_port)		\
144162306a36Sopenharmony_cido {									\
144262306a36Sopenharmony_ci	fifo = usbhsf_get_dnfifo(priv, channel);			\
144362306a36Sopenharmony_ci	fifo->name	= "D"#channel"FIFO";				\
144462306a36Sopenharmony_ci	fifo->port	= fifo_port;					\
144562306a36Sopenharmony_ci	fifo->sel	= D##channel##FIFOSEL;				\
144662306a36Sopenharmony_ci	fifo->ctr	= D##channel##FIFOCTR;				\
144762306a36Sopenharmony_ci	fifo->tx_slave.shdma_slave.slave_id =				\
144862306a36Sopenharmony_ci			usbhs_get_dparam(priv, d##channel##_tx_id);	\
144962306a36Sopenharmony_ci	fifo->rx_slave.shdma_slave.slave_id =				\
145062306a36Sopenharmony_ci			usbhs_get_dparam(priv, d##channel##_rx_id);	\
145162306a36Sopenharmony_ci	usbhsf_dma_init(priv, fifo, channel);				\
145262306a36Sopenharmony_ci} while (0)
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci#define USBHS_DFIFO_INIT(priv, fifo, channel)				\
145562306a36Sopenharmony_ci		__USBHS_DFIFO_INIT(priv, fifo, channel, D##channel##FIFO)
145662306a36Sopenharmony_ci#define USBHS_DFIFO_INIT_NO_PORT(priv, fifo, channel)			\
145762306a36Sopenharmony_ci		__USBHS_DFIFO_INIT(priv, fifo, channel, 0)
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ciint usbhs_fifo_probe(struct usbhs_priv *priv)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	struct usbhs_fifo *fifo;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	/* CFIFO */
146462306a36Sopenharmony_ci	fifo = usbhsf_get_cfifo(priv);
146562306a36Sopenharmony_ci	fifo->name	= "CFIFO";
146662306a36Sopenharmony_ci	fifo->port	= CFIFO;
146762306a36Sopenharmony_ci	fifo->sel	= CFIFOSEL;
146862306a36Sopenharmony_ci	fifo->ctr	= CFIFOCTR;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/* DFIFO */
147162306a36Sopenharmony_ci	USBHS_DFIFO_INIT(priv, fifo, 0);
147262306a36Sopenharmony_ci	USBHS_DFIFO_INIT(priv, fifo, 1);
147362306a36Sopenharmony_ci	USBHS_DFIFO_INIT_NO_PORT(priv, fifo, 2);
147462306a36Sopenharmony_ci	USBHS_DFIFO_INIT_NO_PORT(priv, fifo, 3);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	return 0;
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_civoid usbhs_fifo_remove(struct usbhs_priv *priv)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	struct usbhs_fifo *fifo;
148262306a36Sopenharmony_ci	int i;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	usbhs_for_each_dfifo(priv, fifo, i)
148562306a36Sopenharmony_ci		usbhsf_dma_quit(priv, fifo);
148662306a36Sopenharmony_ci}
1487