renesas_usbhs has CFIFO which is for PIO transfer, and D0FIFO/D1FIFO which are for DMA transfer. The pipe selects one of these fifo when it send/recv data. But fifo must not be selected to different pipe in same time. This patch add pipe/fifo link for each other, and fifo is not selected by another pipe until it is unselected. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@xxxxxxxxxxx> --- drivers/usb/renesas_usbhs/fifo.c | 48 ++++++++++++++++++++++++++++--------- drivers/usb/renesas_usbhs/fifo.h | 2 + drivers/usb/renesas_usbhs/pipe.c | 13 ++++++++++ drivers/usb/renesas_usbhs/pipe.h | 4 +++ 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 53e2b35..8852423 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -21,6 +21,8 @@ #define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo)) +#define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */ + /* * packet info function */ @@ -237,6 +239,15 @@ static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv, return usbhs_read(priv, fifo->ctr) & DTLN_MASK; } +static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe, + struct usbhs_fifo *fifo) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + + usbhs_pipe_select_fifo(pipe, NULL); + usbhs_write(priv, fifo->sel, 0); +} + static int usbhsf_fifo_select(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo, int write) @@ -247,6 +258,10 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, u16 mask = ((1 << 5) | 0xF); /* mask of ISEL | CURPIPE */ u16 base = usbhs_pipe_number(pipe); /* CURPIPE */ + if (usbhs_pipe_is_busy(pipe) || + usbhsf_fifo_is_busy(fifo)) + return -EBUSY; + if (usbhs_pipe_is_dcp(pipe)) base |= (1 == write) << 5; /* ISEL */ @@ -255,8 +270,10 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, /* check ISEL and CURPIPE value */ while (timeout--) { - if (base == (mask & usbhs_read(priv, fifo->sel))) + if (base == (mask & usbhs_read(priv, fifo->sel))) { + usbhs_pipe_select_fifo(pipe, fifo); return 0; + } udelay(10); } @@ -283,7 +300,7 @@ static int usbhsf_try_push(struct usbhs_pkt *pkt, int *is_done) ret = usbhsf_fifo_select(pipe, fifo, 1); if (ret < 0) - goto usbhs_fifo_write_busy; + return 0; ret = usbhs_pipe_is_accessible(pipe); if (ret < 0) @@ -347,9 +364,13 @@ static int usbhsf_try_push(struct usbhs_pkt *pkt, int *is_done) usbhs_dcp_control_transfer_done(pipe); } + usbhsf_fifo_unselect(pipe, fifo); + return 0; usbhs_fifo_write_busy: + usbhsf_fifo_unselect(pipe, fifo); + /* * pipe is busy. * retry in interrupt @@ -367,16 +388,13 @@ struct usbhs_pkt_handle usbhs_fifo_push_handler = { static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) { struct usbhs_pipe *pipe = pkt->pipe; - struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */ - int ret; + + if (usbhs_pipe_is_busy(pipe)) + return 0; /* - * select pipe and enable it to prepare packet receive + * pipe enable to prepare packet receive */ - ret = usbhsf_fifo_select(pipe, fifo, 0); - if (ret < 0) - return ret; usbhs_pipe_enable(pipe); usbhsf_rx_irq_ctrl(pipe, 1); @@ -400,11 +418,11 @@ static int usbhsf_try_pop(struct usbhs_pkt *pkt, int *is_done) ret = usbhsf_fifo_select(pipe, fifo, 0); if (ret < 0) - return ret; + return 0; ret = usbhsf_fifo_barrier(priv, fifo); if (ret < 0) - return ret; + goto usbhs_fifo_read_busy; rcv_len = usbhsf_fifo_rcv_len(priv, fifo); @@ -457,7 +475,10 @@ usbhs_fifo_read_end: usbhs_pipe_number(pipe), pkt->length, pkt->actual, *is_done, pkt->zero); - return 0; +usbhs_fifo_read_busy: + usbhsf_fifo_unselect(pipe, fifo); + + return ret; } struct usbhs_pkt_handle usbhs_fifo_pop_handler = { @@ -551,11 +572,14 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv, void usbhs_fifo_init(struct usbhs_priv *priv) { struct usbhs_mod *mod = usbhs_mod_get_current(priv); + struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv); mod->irq_empty = usbhsf_irq_empty; mod->irq_ready = usbhsf_irq_ready; mod->irq_bempsts = 0; mod->irq_brdysts = 0; + + cfifo->pipe = NULL; } void usbhs_fifo_quit(struct usbhs_priv *priv) diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 04d000a..4292f8c 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -23,6 +23,8 @@ struct usbhs_fifo { u32 port; /* xFIFO */ u32 sel; /* xFIFOSEL */ u32 ctr; /* xFIFOCTR */ + + struct usbhs_pipe *pipe; }; struct usbhs_fifo_info { diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 56137d5..c050587 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -562,6 +562,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv, info->bufnmb_last++; usbhsp_flags_init(pipe); + pipe->fifo = NULL; pipe->mod_private = NULL; INIT_LIST_HEAD(&pipe->list); @@ -620,6 +621,18 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, return pipe; } +void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo) +{ + if (pipe->fifo) + pipe->fifo->pipe = NULL; + + pipe->fifo = fifo; + + if (fifo) + fifo->pipe = pipe; +} + + /* * dcp control */ diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 20e3cf4..484adbe 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -27,6 +27,7 @@ struct usbhs_pipe { u32 pipe_type; /* USB_ENDPOINT_XFER_xxx */ struct usbhs_priv *priv; + struct usbhs_fifo *fifo; struct list_head list; u32 flags; @@ -88,10 +89,13 @@ int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); void usbhs_pipe_stall(struct usbhs_pipe *pipe); +void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); #define usbhs_pipe_to_priv(p) ((p)->priv) #define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe) #define usbhs_pipe_is_dcp(p) ((p)->priv->pipe_info.pipe == (p)) +#define usbhs_pipe_to_fifo(p) ((p)->fifo) +#define usbhs_pipe_is_busy(p) usbhs_pipe_to_fifo(p) /* * dcp control -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html