diff options
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ohci-q.c | 109 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 1 |
4 files changed, 70 insertions, 58 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 52829276a44e..3112799bba7f 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
@@ -780,24 +780,21 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) | |||
780 | usb_hcd_resume_root_hub(hcd); | 780 | usb_hcd_resume_root_hub(hcd); |
781 | } | 781 | } |
782 | 782 | ||
783 | if (ints & OHCI_INTR_WDH) { | 783 | spin_lock(&ohci->lock); |
784 | spin_lock (&ohci->lock); | 784 | if (ints & OHCI_INTR_WDH) |
785 | dl_done_list (ohci); | 785 | update_done_list(ohci); |
786 | spin_unlock (&ohci->lock); | ||
787 | } | ||
788 | 786 | ||
789 | /* could track INTR_SO to reduce available PCI/... bandwidth */ | 787 | /* could track INTR_SO to reduce available PCI/... bandwidth */ |
790 | 788 | ||
791 | /* handle any pending URB/ED unlinks, leaving INTR_SF enabled | 789 | /* handle any pending URB/ED unlinks, leaving INTR_SF enabled |
792 | * when there's still unlinking to be done (next frame). | 790 | * when there's still unlinking to be done (next frame). |
793 | */ | 791 | */ |
794 | spin_lock (&ohci->lock); | 792 | process_done_list(ohci); |
795 | if (ohci->ed_rm_list) | 793 | if (ohci->ed_rm_list) |
796 | finish_unlinks (ohci, ohci_frame_no(ohci)); | 794 | finish_unlinks (ohci, ohci_frame_no(ohci)); |
797 | if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list | 795 | if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list |
798 | && ohci->rh_state == OHCI_RH_RUNNING) | 796 | && ohci->rh_state == OHCI_RH_RUNNING) |
799 | ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); | 797 | ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); |
800 | spin_unlock (&ohci->lock); | ||
801 | 798 | ||
802 | if (ohci->rh_state == OHCI_RH_RUNNING) { | 799 | if (ohci->rh_state == OHCI_RH_RUNNING) { |
803 | ohci_writel (ohci, ints, ®s->intrstatus); | 800 | ohci_writel (ohci, ints, ®s->intrstatus); |
@@ -805,6 +802,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) | |||
805 | // flush those writes | 802 | // flush those writes |
806 | (void) ohci_readl (ohci, &ohci->regs->control); | 803 | (void) ohci_readl (ohci, &ohci->regs->control); |
807 | } | 804 | } |
805 | spin_unlock(&ohci->lock); | ||
808 | 806 | ||
809 | return IRQ_HANDLED; | 807 | return IRQ_HANDLED; |
810 | } | 808 | } |
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index b4940de1eba3..dccb90edd66e 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -39,7 +39,8 @@ | |||
39 | #define OHCI_SCHED_ENABLES \ | 39 | #define OHCI_SCHED_ENABLES \ |
40 | (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) | 40 | (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) |
41 | 41 | ||
42 | static void dl_done_list (struct ohci_hcd *); | 42 | static void update_done_list(struct ohci_hcd *); |
43 | static void process_done_list(struct ohci_hcd *); | ||
43 | static void finish_unlinks (struct ohci_hcd *, u16); | 44 | static void finish_unlinks (struct ohci_hcd *, u16); |
44 | 45 | ||
45 | #ifdef CONFIG_PM | 46 | #ifdef CONFIG_PM |
@@ -87,7 +88,8 @@ __acquires(ohci->lock) | |||
87 | msleep (8); | 88 | msleep (8); |
88 | spin_lock_irq (&ohci->lock); | 89 | spin_lock_irq (&ohci->lock); |
89 | } | 90 | } |
90 | dl_done_list (ohci); | 91 | update_done_list(ohci); |
92 | process_done_list(ohci); | ||
91 | finish_unlinks (ohci, ohci_frame_no(ohci)); | 93 | finish_unlinks (ohci, ohci_frame_no(ohci)); |
92 | 94 | ||
93 | /* | 95 | /* |
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index a9f4f04c3fad..f36b2fa0ee2f 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c | |||
@@ -892,13 +892,41 @@ static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc) | |||
892 | } | 892 | } |
893 | } | 893 | } |
894 | 894 | ||
895 | /* replies to the request have to be on a FIFO basis so | 895 | /* Add a TD to the done list */ |
896 | * we unreverse the hc-reversed done-list | 896 | static void add_to_done_list(struct ohci_hcd *ohci, struct td *td) |
897 | */ | 897 | { |
898 | static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) | 898 | struct td *td2, *td_prev; |
899 | struct ed *ed; | ||
900 | |||
901 | if (td->next_dl_td) | ||
902 | return; /* Already on the list */ | ||
903 | |||
904 | /* Add all the TDs going back until we reach one that's on the list */ | ||
905 | ed = td->ed; | ||
906 | td2 = td_prev = td; | ||
907 | list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) { | ||
908 | if (td2->next_dl_td) | ||
909 | break; | ||
910 | td2->next_dl_td = td_prev; | ||
911 | td_prev = td2; | ||
912 | } | ||
913 | |||
914 | if (ohci->dl_end) | ||
915 | ohci->dl_end->next_dl_td = td_prev; | ||
916 | else | ||
917 | ohci->dl_start = td_prev; | ||
918 | |||
919 | /* | ||
920 | * Make td->next_dl_td point to td itself, to mark the fact | ||
921 | * that td is on the done list. | ||
922 | */ | ||
923 | ohci->dl_end = td->next_dl_td = td; | ||
924 | } | ||
925 | |||
926 | /* Get the entries on the hardware done queue and put them on our list */ | ||
927 | static void update_done_list(struct ohci_hcd *ohci) | ||
899 | { | 928 | { |
900 | u32 td_dma; | 929 | u32 td_dma; |
901 | struct td *td_rev = NULL; | ||
902 | struct td *td = NULL; | 930 | struct td *td = NULL; |
903 | 931 | ||
904 | td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head); | 932 | td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head); |
@@ -906,7 +934,7 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) | |||
906 | wmb(); | 934 | wmb(); |
907 | 935 | ||
908 | /* get TD from hc's singly linked list, and | 936 | /* get TD from hc's singly linked list, and |
909 | * prepend to ours. ed->td_list changes later. | 937 | * add to ours. ed->td_list changes later. |
910 | */ | 938 | */ |
911 | while (td_dma) { | 939 | while (td_dma) { |
912 | int cc; | 940 | int cc; |
@@ -928,11 +956,9 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) | |||
928 | && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H))) | 956 | && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H))) |
929 | ed_halted(ohci, td, cc); | 957 | ed_halted(ohci, td, cc); |
930 | 958 | ||
931 | td->next_dl_td = td_rev; | ||
932 | td_rev = td; | ||
933 | td_dma = hc32_to_cpup (ohci, &td->hwNextTD); | 959 | td_dma = hc32_to_cpup (ohci, &td->hwNextTD); |
960 | add_to_done_list(ohci, td); | ||
934 | } | 961 | } |
935 | return td_rev; | ||
936 | } | 962 | } |
937 | 963 | ||
938 | /*-------------------------------------------------------------------------*/ | 964 | /*-------------------------------------------------------------------------*/ |
@@ -956,26 +982,27 @@ rescan_all: | |||
956 | /* only take off EDs that the HC isn't using, accounting for | 982 | /* only take off EDs that the HC isn't using, accounting for |
957 | * frame counter wraps and EDs with partially retired TDs | 983 | * frame counter wraps and EDs with partially retired TDs |
958 | */ | 984 | */ |
959 | if (likely(ohci->rh_state == OHCI_RH_RUNNING)) { | 985 | if (likely(ohci->rh_state == OHCI_RH_RUNNING) && |
960 | if (tick_before (tick, ed->tick)) { | 986 | tick_before(tick, ed->tick)) { |
961 | skip_ed: | 987 | skip_ed: |
962 | last = &ed->ed_next; | 988 | last = &ed->ed_next; |
963 | continue; | 989 | continue; |
964 | } | 990 | } |
991 | if (!list_empty(&ed->td_list)) { | ||
992 | struct td *td; | ||
993 | u32 head; | ||
965 | 994 | ||
966 | if (!list_empty (&ed->td_list)) { | 995 | td = list_first_entry(&ed->td_list, struct td, td_list); |
967 | struct td *td; | ||
968 | u32 head; | ||
969 | 996 | ||
970 | td = list_entry (ed->td_list.next, struct td, | 997 | /* INTR_WDH may need to clean up first */ |
971 | td_list); | 998 | head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK; |
972 | head = hc32_to_cpu (ohci, ed->hwHeadP) & | 999 | if (td->td_dma != head && |
973 | TD_MASK; | 1000 | ohci->rh_state == OHCI_RH_RUNNING) |
1001 | goto skip_ed; | ||
974 | 1002 | ||
975 | /* INTR_WDH may need to clean up first */ | 1003 | /* Don't mess up anything already on the done list */ |
976 | if (td->td_dma != head) | 1004 | if (td->next_dl_td) |
977 | goto skip_ed; | 1005 | goto skip_ed; |
978 | } | ||
979 | } | 1006 | } |
980 | 1007 | ||
981 | /* ED's now officially unlinked, hc doesn't see */ | 1008 | /* ED's now officially unlinked, hc doesn't see */ |
@@ -1161,33 +1188,17 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td) | |||
1161 | * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list, | 1188 | * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list, |
1162 | * instead of scanning the (re-reversed) donelist as this does. | 1189 | * instead of scanning the (re-reversed) donelist as this does. |
1163 | */ | 1190 | */ |
1164 | static void | 1191 | static void process_done_list(struct ohci_hcd *ohci) |
1165 | dl_done_list (struct ohci_hcd *ohci) | ||
1166 | { | 1192 | { |
1167 | struct td *td = dl_reverse_done_list (ohci); | 1193 | struct td *td; |
1168 | |||
1169 | while (td) { | ||
1170 | struct td *td_next = td->next_dl_td; | ||
1171 | struct ed *ed = td->ed; | ||
1172 | 1194 | ||
1173 | /* | 1195 | while (ohci->dl_start) { |
1174 | * Some OHCI controllers (NVIDIA for sure, maybe others) | 1196 | td = ohci->dl_start; |
1175 | * occasionally forget to add TDs to the done queue. Since | 1197 | if (td == ohci->dl_end) |
1176 | * TDs for a given endpoint are always processed in order, | 1198 | ohci->dl_start = ohci->dl_end = NULL; |
1177 | * if we find a TD on the donelist then all of its | 1199 | else |
1178 | * predecessors must be finished as well. | 1200 | ohci->dl_start = td->next_dl_td; |
1179 | */ | ||
1180 | for (;;) { | ||
1181 | struct td *td2; | ||
1182 | |||
1183 | td2 = list_first_entry(&ed->td_list, struct td, | ||
1184 | td_list); | ||
1185 | if (td2 == td) | ||
1186 | break; | ||
1187 | takeback_td(ohci, td2); | ||
1188 | } | ||
1189 | 1201 | ||
1190 | takeback_td(ohci, td); | 1202 | takeback_td(ohci, td); |
1191 | td = td_next; | ||
1192 | } | 1203 | } |
1193 | } | 1204 | } |
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 392932dd6318..a8259bc6fd8b 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h | |||
@@ -380,6 +380,7 @@ struct ohci_hcd { | |||
380 | struct dma_pool *td_cache; | 380 | struct dma_pool *td_cache; |
381 | struct dma_pool *ed_cache; | 381 | struct dma_pool *ed_cache; |
382 | struct td *td_hash [TD_HASH_SIZE]; | 382 | struct td *td_hash [TD_HASH_SIZE]; |
383 | struct td *dl_start, *dl_end; /* the done list */ | ||
383 | struct list_head pending; | 384 | struct list_head pending; |
384 | 385 | ||
385 | /* | 386 | /* |