diff options
author | Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> | 2014-08-22 07:13:50 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-09-03 10:15:57 -0400 |
commit | 8355b2b3082d302091506703d2e4e239f7deed7f (patch) | |
tree | 7d883ae6ca66cb0778b45692bd00bf1342cb20fd /drivers/usb | |
parent | f0798d6a04867ad8db8b41ddea45627d2c8cffe3 (diff) |
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>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.c | 25 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/pipe.c | 13 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/pipe.h | 4 |
3 files changed, 41 insertions, 1 deletions
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 4fd36530bfa3..3efece3c72a3 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c | |||
@@ -544,6 +544,7 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) | |||
544 | usbhsf_send_terminator(pipe, fifo); | 544 | usbhsf_send_terminator(pipe, fifo); |
545 | 545 | ||
546 | usbhsf_tx_irq_ctrl(pipe, !*is_done); | 546 | usbhsf_tx_irq_ctrl(pipe, !*is_done); |
547 | usbhs_pipe_running(pipe, !*is_done); | ||
547 | usbhs_pipe_enable(pipe); | 548 | usbhs_pipe_enable(pipe); |
548 | 549 | ||
549 | dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", | 550 | dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n", |
@@ -570,12 +571,21 @@ usbhs_fifo_write_busy: | |||
570 | * retry in interrupt | 571 | * retry in interrupt |
571 | */ | 572 | */ |
572 | usbhsf_tx_irq_ctrl(pipe, 1); | 573 | usbhsf_tx_irq_ctrl(pipe, 1); |
574 | usbhs_pipe_running(pipe, 1); | ||
573 | 575 | ||
574 | return ret; | 576 | return ret; |
575 | } | 577 | } |
576 | 578 | ||
579 | static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done) | ||
580 | { | ||
581 | if (usbhs_pipe_is_running(pkt->pipe)) | ||
582 | return 0; | ||
583 | |||
584 | return usbhsf_pio_try_push(pkt, is_done); | ||
585 | } | ||
586 | |||
577 | struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { | 587 | struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { |
578 | .prepare = usbhsf_pio_try_push, | 588 | .prepare = usbhsf_pio_prepare_push, |
579 | .try_run = usbhsf_pio_try_push, | 589 | .try_run = usbhsf_pio_try_push, |
580 | }; | 590 | }; |
581 | 591 | ||
@@ -589,6 +599,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) | |||
589 | if (usbhs_pipe_is_busy(pipe)) | 599 | if (usbhs_pipe_is_busy(pipe)) |
590 | return 0; | 600 | return 0; |
591 | 601 | ||
602 | if (usbhs_pipe_is_running(pipe)) | ||
603 | return 0; | ||
604 | |||
592 | /* | 605 | /* |
593 | * pipe enable to prepare packet receive | 606 | * pipe enable to prepare packet receive |
594 | */ | 607 | */ |
@@ -597,6 +610,7 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) | |||
597 | 610 | ||
598 | usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length); | 611 | usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length); |
599 | usbhs_pipe_enable(pipe); | 612 | usbhs_pipe_enable(pipe); |
613 | usbhs_pipe_running(pipe, 1); | ||
600 | usbhsf_rx_irq_ctrl(pipe, 1); | 614 | usbhsf_rx_irq_ctrl(pipe, 1); |
601 | 615 | ||
602 | return 0; | 616 | return 0; |
@@ -642,6 +656,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) | |||
642 | (total_len < maxp)) { /* short packet */ | 656 | (total_len < maxp)) { /* short packet */ |
643 | *is_done = 1; | 657 | *is_done = 1; |
644 | usbhsf_rx_irq_ctrl(pipe, 0); | 658 | usbhsf_rx_irq_ctrl(pipe, 0); |
659 | usbhs_pipe_running(pipe, 0); | ||
645 | usbhs_pipe_disable(pipe); /* disable pipe first */ | 660 | usbhs_pipe_disable(pipe); /* disable pipe first */ |
646 | } | 661 | } |
647 | 662 | ||
@@ -805,6 +820,7 @@ static void xfer_work(struct work_struct *work) | |||
805 | dev_dbg(dev, " %s %d (%d/ %d)\n", | 820 | dev_dbg(dev, " %s %d (%d/ %d)\n", |
806 | fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); | 821 | fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); |
807 | 822 | ||
823 | usbhs_pipe_running(pipe, 1); | ||
808 | usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); | 824 | usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); |
809 | usbhs_pipe_enable(pipe); | 825 | usbhs_pipe_enable(pipe); |
810 | usbhsf_dma_start(pipe, fifo); | 826 | usbhsf_dma_start(pipe, fifo); |
@@ -836,6 +852,10 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) | |||
836 | if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ | 852 | if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ |
837 | goto usbhsf_pio_prepare_push; | 853 | goto usbhsf_pio_prepare_push; |
838 | 854 | ||
855 | /* return at this time if the pipe is running */ | ||
856 | if (usbhs_pipe_is_running(pipe)) | ||
857 | return 0; | ||
858 | |||
839 | /* get enable DMA fifo */ | 859 | /* get enable DMA fifo */ |
840 | fifo = usbhsf_get_dma_fifo(priv, pkt); | 860 | fifo = usbhsf_get_dma_fifo(priv, pkt); |
841 | if (!fifo) | 861 | if (!fifo) |
@@ -873,6 +893,7 @@ static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done) | |||
873 | pkt->actual = pkt->trans; | 893 | pkt->actual = pkt->trans; |
874 | 894 | ||
875 | *is_done = !pkt->zero; /* send zero packet ? */ | 895 | *is_done = !pkt->zero; /* send zero packet ? */ |
896 | usbhs_pipe_running(pipe, !*is_done); | ||
876 | 897 | ||
877 | usbhsf_dma_stop(pipe, pipe->fifo); | 898 | usbhsf_dma_stop(pipe, pipe->fifo); |
878 | usbhsf_dma_unmap(pkt); | 899 | usbhsf_dma_unmap(pkt); |
@@ -972,8 +993,10 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) | |||
972 | if ((pkt->actual == pkt->length) || /* receive all data */ | 993 | if ((pkt->actual == pkt->length) || /* receive all data */ |
973 | (pkt->trans < maxp)) { /* short packet */ | 994 | (pkt->trans < maxp)) { /* short packet */ |
974 | *is_done = 1; | 995 | *is_done = 1; |
996 | usbhs_pipe_running(pipe, 0); | ||
975 | } else { | 997 | } else { |
976 | /* re-enable */ | 998 | /* re-enable */ |
999 | usbhs_pipe_running(pipe, 0); | ||
977 | usbhsf_prepare_pop(pkt, is_done); | 1000 | usbhsf_prepare_pop(pkt, is_done); |
978 | } | 1001 | } |
979 | 1002 | ||
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 75fbcf6b102e..040bcefcb040 100644 --- 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_pipe *pipe) | |||
578 | return usbhsp_flags_has(pipe, IS_DIR_HOST); | 578 | return usbhsp_flags_has(pipe, IS_DIR_HOST); |
579 | } | 579 | } |
580 | 580 | ||
581 | int usbhs_pipe_is_running(struct usbhs_pipe *pipe) | ||
582 | { | ||
583 | return usbhsp_flags_has(pipe, IS_RUNNING); | ||
584 | } | ||
585 | |||
586 | void usbhs_pipe_running(struct usbhs_pipe *pipe, int running) | ||
587 | { | ||
588 | if (running) | ||
589 | usbhsp_flags_set(pipe, IS_RUNNING); | ||
590 | else | ||
591 | usbhsp_flags_clr(pipe, IS_RUNNING); | ||
592 | } | ||
593 | |||
581 | void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) | 594 | void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) |
582 | { | 595 | { |
583 | u16 mask = (SQCLR | SQSET); | 596 | u16 mask = (SQCLR | SQSET); |
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 406f36d050e4..d24a05972370 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h | |||
@@ -36,6 +36,7 @@ struct usbhs_pipe { | |||
36 | #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) | 36 | #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) |
37 | #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) | 37 | #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) |
38 | #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) | 38 | #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) |
39 | #define USBHS_PIPE_FLAGS_IS_RUNNING (1 << 3) | ||
39 | 40 | ||
40 | struct usbhs_pkt_handle *handler; | 41 | struct usbhs_pkt_handle *handler; |
41 | 42 | ||
@@ -80,6 +81,9 @@ int usbhs_pipe_probe(struct usbhs_priv *priv); | |||
80 | void usbhs_pipe_remove(struct usbhs_priv *priv); | 81 | void usbhs_pipe_remove(struct usbhs_priv *priv); |
81 | int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); | 82 | int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); |
82 | int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); | 83 | int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); |
84 | int usbhs_pipe_is_running(struct usbhs_pipe *pipe); | ||
85 | void usbhs_pipe_running(struct usbhs_pipe *pipe, int running); | ||
86 | |||
83 | void usbhs_pipe_init(struct usbhs_priv *priv, | 87 | void usbhs_pipe_init(struct usbhs_priv *priv, |
84 | int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); | 88 | int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); |
85 | int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); | 89 | int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); |