]> git.hungrycats.org Git - linux/commitdiff
dwc_otg: fix NAK holdoff and allow on split transactions only
authorP33M <P33M@github.com>
Sun, 21 Apr 2013 23:08:36 +0000 (00:08 +0100)
committerP33M <P33M@github.com>
Sun, 21 Apr 2013 23:08:36 +0000 (00:08 +0100)
This corrects a bug where if a single active non-periodic endpoint
had at least one transaction in its qh, on frnum == MAX_FRNUM the qh
would get skipped and never get queued again. This would result in
a silent device until error detection (automatic or otherwise) would
either reset the device or flush and requeue the URBs.

Additionally the NAK holdoff was enabled for all transactions - this
would potentially stall a HS endpoint for 1ms if a previous error state
enabled this interrupt and the next response was a NAK. Fix so that
only split transactions get held off.

drivers/usb/host/dwc_otg/dwc_otg_hcd.c

index 91eefecd0dbcb5ce375e597df9666bee2ea5d164..eaa8f384649ac65205e4e5f3b39f3227e0e25c92 100644 (file)
@@ -46,7 +46,7 @@
 #include "dwc_otg_hcd.h"
 #include "dwc_otg_regs.h"
 
-extern bool microframe_schedule;
+extern bool microframe_schedule, nak_holdoff_enable;
 
 //#define DEBUG_HOST_CHANNELS
 #ifdef DEBUG_HOST_CHANNELS
@@ -1349,18 +1349,26 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
 
                /*
                 * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission
-                * we hold off on bulk retransmissions to reduce NAK interrupt overhead for
+                * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed
                 * cheeky devices that just hold off using NAKs
                 */
-               if (dwc_full_frame_num(qh->nak_frame) == dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) {
-                       // Make fiq interrupt run on next frame (i.e. 8 uframes)
-                       g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM;
-                       qh_ptr = DWC_LIST_NEXT(qh_ptr);
-                       continue;
+               if (nak_holdoff_enable && qh->do_split) {
+                       if (qh->nak_frame != 0xffff &&
+                               dwc_full_frame_num(qh->nak_frame) ==
+                               dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) {
+                               /*
+                                * Revisit: Need to avoid trampling on periodic scheduling.
+                                * Currently we are safe because g_np_count != g_np_sent whenever we hit this,
+                                * but if this behaviour is changed then periodic endpoints will get a slower
+                                * polling rate.
+                                */
+                               g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM;
+                               qh_ptr = DWC_LIST_NEXT(qh_ptr);
+                               continue;
+                       } else {
+                               qh->nak_frame = 0xffff;
+                       }
                }
-               else
-                       qh->nak_frame = 0xffff;
-
                if (microframe_schedule) {
                                DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
                                if (hcd->available_host_channels < 1) {