diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index ffdf5d15085e..b51fcd80d244 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -56,7 +56,7 @@ static struct usbhs_pkt_handle usbhsf_null_handler = { void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt), - void *buf, int len, int zero) + void *buf, int len, int zero, int sequence) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct device *dev = usbhs_priv_to_dev(priv); @@ -90,6 +90,7 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, pkt->zero = zero; pkt->actual = 0; pkt->done = done; + pkt->sequence = sequence; usbhs_unlock(priv, flags); /******************** spin unlock ******************/ @@ -481,6 +482,9 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) int i, ret, len; int is_short; + usbhs_pipe_data_sequence(pipe, pkt->sequence); + pkt->sequence = -1; /* -1 sequence will be ignored */ + ret = usbhsf_fifo_select(pipe, fifo, 1); if (ret < 0) return 0; @@ -584,6 +588,8 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) /* * pipe enable to prepare packet receive */ + usbhs_pipe_data_sequence(pipe, pkt->sequence); + pkt->sequence = -1; /* -1 sequence will be ignored */ usbhs_pipe_enable(pipe); usbhsf_rx_irq_ctrl(pipe, 1); @@ -641,6 +647,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function" */ if (0 == rcv_len) { + pkt->zero = 1; usbhsf_fifo_clear(pipe, fifo); goto usbhs_fifo_read_end; } diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 32a7b246b28d..f68609c0f489 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -59,6 +59,7 @@ struct usbhs_pkt { int trans; int actual; int zero; + int sequence; }; struct usbhs_pkt_handle { @@ -95,7 +96,7 @@ void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt), - void *buf, int len, int zero); + void *buf, int len, int zero, int sequence); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); void usbhs_pkt_start(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index cf3141c330a3..db2a1c6a0866 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -154,7 +154,7 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep, req->actual = 0; req->status = -EINPROGRESS; usbhs_pkt_push(pipe, pkt, usbhsg_queue_done, - req->buf, req->length, req->zero); + req->buf, req->length, req->zero, -1); usbhs_pkt_start(pipe); dev_dbg(dev, "pipe %d : queue push (%d)\n", diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index c7f9be9f5c17..72ee8e55e717 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -193,6 +193,48 @@ static void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv, /* * pipe control */ +static void usbhsh_endpoint_sequence_save(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pkt *pkt) +{ + int len = urb->actual_length; + int maxp = usb_endpoint_maxp(&urb->ep->desc); + int t = 0; + + /* DCP is out of sequence control */ + if (usb_pipecontrol(urb->pipe)) + return; + + /* + * renesas_usbhs pipe has a limitation in a number. + * So, driver should re-use the limited pipe for each device/endpoint. + * DATA0/1 sequence should be saved for it. + * see [image of mod_host] + * [HARDWARE LIMITATION] + */ + + /* + * next sequence depends on actual_length + * + * ex) actual_length = 1147, maxp = 512 + * data0 : 512 + * data1 : 512 + * data0 : 123 + * data1 is the next sequence + */ + t = len / maxp; + if (len % maxp) + t++; + if (pkt->zero) + t++; + t %= 2; + + if (t) + usb_dotoggle(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); +} + static struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv, struct urb *urb); @@ -247,15 +289,6 @@ static int usbhsh_pipe_attach(struct usbhsh_hpriv *hpriv, usbhsh_uep_to_pipe(uep) = pipe; usbhsh_pipe_to_uep(pipe) = uep; - if (!usb_gettoggle(urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe))) { - usbhs_pipe_sequence_data0(pipe); - usb_settoggle(urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), 1); - } - usbhs_pipe_config_update(pipe, usbhsh_device_number(hpriv, udev), usb_endpoint_num(desc), @@ -598,10 +631,11 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) urb->actual_length = pkt->actual; usbhsh_ureq_free(hpriv, ureq); + usbhsh_endpoint_sequence_save(hpriv, urb, pkt); + usbhsh_pipe_detach(hpriv, uep); + usb_hcd_unlink_urb_from_ep(hcd, urb); usb_hcd_giveback_urb(hcd, urb, 0); - - usbhsh_pipe_detach(hpriv, uep); } static int usbhsh_queue_push(struct usb_hcd *hcd, @@ -614,7 +648,7 @@ static int usbhsh_queue_push(struct usb_hcd *hcd, struct device *dev = usbhsh_hcd_to_dev(hcd); struct usbhsh_request *ureq; void *buf; - int len; + int len, sequence; if (usb_pipeisoc(urb->pipe)) { dev_err(dev, "pipe iso is not supported now\n"); @@ -636,9 +670,15 @@ static int usbhsh_queue_push(struct usb_hcd *hcd, buf = (void *)(urb->transfer_buffer + urb->actual_length); len = urb->transfer_buffer_length - urb->actual_length; + sequence = usb_gettoggle(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + dev_dbg(dev, "%s\n", __func__); usbhs_pkt_push(pipe, &ureq->pkt, usbhsh_queue_done, - buf, len, (urb->transfer_flags & URB_ZERO_PACKET)); + buf, len, (urb->transfer_flags & URB_ZERO_PACKET), + sequence); + usbhs_pkt_start(pipe); return 0; @@ -741,7 +781,8 @@ static int usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv, usbhsh_data_stage_packet_done, urb->transfer_buffer, urb->transfer_buffer_length, - (urb->transfer_flags & URB_ZERO_PACKET)); + (urb->transfer_flags & URB_ZERO_PACKET), + -1); return 0; } @@ -770,7 +811,7 @@ static int usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv, usbhsh_queue_done, NULL, urb->transfer_buffer_length, - 0); + 0, -1); return 0; } diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index c36ad4c3a259..c2559e80d41f 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -478,10 +478,27 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe) return usbhsp_flags_has(pipe, IS_DIR_HOST); } -void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data) +void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) { u16 mask = (SQCLR | SQSET); - u16 val = (data) ? SQSET : SQCLR; + u16 val; + + /* + * sequence + * 0 : data0 + * 1 : data1 + * -1 : no change + */ + switch (sequence) { + case 0: + val = SQCLR; + break; + case 1: + val = SQSET; + break; + default: + return; + } usbhsp_pipectrl_set(pipe, mask, val); }