| From d20f9492d163be389b2f60911d2894696c54b755 Mon Sep 17 00:00:00 2001 |
| From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> |
| Date: Fri, 22 Aug 2014 20:13:50 +0900 |
| Subject: usb: renesas_usbhs: fix the behavior of some usbhs_pkt_handle |
| |
| Some gadget drivers will call usb_ep_queue() more than once before |
| the first queue doesn't finish. However, this driver didn't handle |
| it correctly. So, this patch fixes the behavior of some |
| usbhs_pkt_handle using the "running" flag. Otherwise, the oops below |
| happens if we use g_ncm driver and when the "iperf -u -c host -b 200M" |
| is running. |
| |
| Unable to handle kernel NULL pointer dereference at virtual address 00000000 |
| pgd = c0004000 |
| [00000000] *pgd=00000000 |
| Internal error: Oops: 80000007 [#1] SMP ARM |
| Modules linked in: usb_f_ncm g_ncm libcomposite u_ether |
| CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.17.0-rc1-00008-g8b2be8a-dirty #20 |
| task: c051c7e0 ti: c0512000 task.ti: c0512000 |
| PC is at 0x0 |
| LR is at usbhsf_pkt_handler+0xa8/0x114 |
| pc : [<00000000>] lr : [<c0278fb4>] psr: 60000193 |
| sp : c0513ce8 ip : c0513c58 fp : c0513d24 |
| r10: 00000001 r9 : 00000193 r8 : eebec4a0 |
| r7 : eebec410 r6 : eebe0c6c r5 : 00000000 r4 : ee4a2774 |
| r3 : 00000000 r2 : ee251e00 r1 : c0513cf4 r0 : ee4a2774 |
| |
| Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> |
| Signed-off-by: Felipe Balbi <balbi@ti.com> |
| (cherry picked from commit 8355b2b3082d302091506703d2e4e239f7deed7f) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/usb/renesas_usbhs/fifo.c | 25 ++++++++++++++++++++++++- |
| drivers/usb/renesas_usbhs/pipe.c | 13 +++++++++++++ |
| drivers/usb/renesas_usbhs/pipe.h | 4 ++++ |
| 3 files changed, 41 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/usb/renesas_usbhs/fifo.c |
| +++ b/drivers/usb/renesas_usbhs/fifo.c |
| @@ -545,6 +545,7 @@ static int usbhsf_pio_try_push(struct us |
| usbhsf_send_terminator(pipe, fifo); |
| |
| usbhsf_tx_irq_ctrl(pipe, !*is_done); |
| + usbhs_pipe_running(pipe, !*is_done); |
| usbhs_pipe_enable(pipe); |
| |
| dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", |
| @@ -571,12 +572,21 @@ usbhs_fifo_write_busy: |
| * retry in interrupt |
| */ |
| usbhsf_tx_irq_ctrl(pipe, 1); |
| + usbhs_pipe_running(pipe, 1); |
| |
| return ret; |
| } |
| |
| +static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done) |
| +{ |
| + if (usbhs_pipe_is_running(pkt->pipe)) |
| + return 0; |
| + |
| + return usbhsf_pio_try_push(pkt, is_done); |
| +} |
| + |
| struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { |
| - .prepare = usbhsf_pio_try_push, |
| + .prepare = usbhsf_pio_prepare_push, |
| .try_run = usbhsf_pio_try_push, |
| }; |
| |
| @@ -590,6 +600,9 @@ static int usbhsf_prepare_pop(struct usb |
| if (usbhs_pipe_is_busy(pipe)) |
| return 0; |
| |
| + if (usbhs_pipe_is_running(pipe)) |
| + return 0; |
| + |
| /* |
| * pipe enable to prepare packet receive |
| */ |
| @@ -598,6 +611,7 @@ static int usbhsf_prepare_pop(struct usb |
| |
| usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length); |
| usbhs_pipe_enable(pipe); |
| + usbhs_pipe_running(pipe, 1); |
| usbhsf_rx_irq_ctrl(pipe, 1); |
| |
| return 0; |
| @@ -643,6 +657,7 @@ static int usbhsf_pio_try_pop(struct usb |
| (total_len < maxp)) { /* short packet */ |
| *is_done = 1; |
| usbhsf_rx_irq_ctrl(pipe, 0); |
| + usbhs_pipe_running(pipe, 0); |
| usbhs_pipe_disable(pipe); /* disable pipe first */ |
| } |
| |
| @@ -806,6 +821,7 @@ static void xfer_work(struct work_struct |
| dev_dbg(dev, " %s %d (%d/ %d)\n", |
| fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); |
| |
| + usbhs_pipe_running(pipe, 1); |
| usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); |
| usbhs_pipe_enable(pipe); |
| usbhsf_dma_start(pipe, fifo); |
| @@ -837,6 +853,10 @@ static int usbhsf_dma_prepare_push(struc |
| if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ |
| goto usbhsf_pio_prepare_push; |
| |
| + /* return at this time if the pipe is running */ |
| + if (usbhs_pipe_is_running(pipe)) |
| + return 0; |
| + |
| /* get enable DMA fifo */ |
| fifo = usbhsf_get_dma_fifo(priv, pkt); |
| if (!fifo) |
| @@ -874,6 +894,7 @@ static int usbhsf_dma_push_done(struct u |
| pkt->actual = pkt->trans; |
| |
| *is_done = !pkt->zero; /* send zero packet ? */ |
| + usbhs_pipe_running(pipe, !*is_done); |
| |
| usbhsf_dma_stop(pipe, pipe->fifo); |
| usbhsf_dma_unmap(pkt); |
| @@ -974,8 +995,10 @@ static int usbhsf_dma_pop_done(struct us |
| if ((pkt->actual == pkt->length) || /* receive all data */ |
| (pkt->trans < maxp)) { /* short packet */ |
| *is_done = 1; |
| + usbhs_pipe_running(pipe, 0); |
| } else { |
| /* re-enable */ |
| + usbhs_pipe_running(pipe, 0); |
| usbhsf_prepare_pop(pkt, is_done); |
| } |
| |
| --- a/drivers/usb/renesas_usbhs/pipe.c |
| +++ b/drivers/usb/renesas_usbhs/pipe.c |
| @@ -578,6 +578,19 @@ int usbhs_pipe_is_dir_host(struct usbhs_ |
| return usbhsp_flags_has(pipe, IS_DIR_HOST); |
| } |
| |
| +int usbhs_pipe_is_running(struct usbhs_pipe *pipe) |
| +{ |
| + return usbhsp_flags_has(pipe, IS_RUNNING); |
| +} |
| + |
| +void usbhs_pipe_running(struct usbhs_pipe *pipe, int running) |
| +{ |
| + if (running) |
| + usbhsp_flags_set(pipe, IS_RUNNING); |
| + else |
| + usbhsp_flags_clr(pipe, IS_RUNNING); |
| +} |
| + |
| void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) |
| { |
| u16 mask = (SQCLR | SQSET); |
| --- a/drivers/usb/renesas_usbhs/pipe.h |
| +++ b/drivers/usb/renesas_usbhs/pipe.h |
| @@ -36,6 +36,7 @@ struct usbhs_pipe { |
| #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) |
| #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) |
| #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) |
| +#define USBHS_PIPE_FLAGS_IS_RUNNING (1 << 3) |
| |
| struct usbhs_pkt_handle *handler; |
| |
| @@ -80,6 +81,9 @@ int usbhs_pipe_probe(struct usbhs_priv * |
| void usbhs_pipe_remove(struct usbhs_priv *priv); |
| int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); |
| int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); |
| +int usbhs_pipe_is_running(struct usbhs_pipe *pipe); |
| +void usbhs_pipe_running(struct usbhs_pipe *pipe, int running); |
| + |
| void usbhs_pipe_init(struct usbhs_priv *priv, |
| int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); |
| int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); |