Updating the TRB_CYCLE bit in the first trb has to be done after all the subsequent trb have been written. The code to do this was overly complicated. When initially writing the start_trb invert its TRB_CYCLE bit. In giveback_first_trb() invert the TRB_CYCLE bit again. Tested with a USB keyboard and the smsc95xx ethernet driver. Signed-off-by: David Laight <david.laight@xxxxxxxxxx> --- I hope this patch (and the others I've sent recently) are properly formatted. They are the first Linux patches I've written. I think this one depends on the earlier s/1/TRB_CYCLE/ change. David drivers/usb/host/xhci-ring.c | 68 +++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 408978b..d0ef8b8 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3077,7 +3077,7 @@ static void check_trb_math(struct urb *urb, int num_trbs, int running_total) } static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, - unsigned int ep_index, unsigned int stream_id, int start_cycle, + unsigned int ep_index, unsigned int stream_id, struct xhci_generic_trb *start_trb) { /* @@ -3085,10 +3085,7 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, * isn't reordered. */ wmb(); - if (start_cycle) - start_trb->field[3] |= cpu_to_le32(start_cycle); - else - start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); + start_trb->field[3] ^= cpu_to_le32(TRB_CYCLE); xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); } @@ -3191,12 +3188,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int num_sgs; int trb_buff_len, this_sg_len, running_total; unsigned int total_packet_count; - bool first_trb; + u32 trb_cycle_invert; u64 addr; bool more_trbs_coming; struct xhci_generic_trb *start_trb; - int start_cycle; ep_ring = xhci_urb_to_transfer_ring(xhci, urb); if (!ep_ring) @@ -3222,7 +3218,6 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, * state may change as we enqueue the other TRBs, so save it too. */ start_trb = &ep_ring->enqueue->generic; - start_cycle = ep_ring->cycle_state; running_total = 0; /* @@ -3242,20 +3237,17 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (trb_buff_len > urb->transfer_buffer_length) trb_buff_len = urb->transfer_buffer_length; - first_trb = true; + trb_cycle_invert = TRB_CYCLE; /* Queue the first TRB, even if it's zero-length */ do { u32 field = 0; u32 length_field = 0; u32 remainder = 0; + field |= ep_ring->cycle_state; /* Don't change the cycle bit of the first TRB until later */ - if (first_trb) { - first_trb = false; - if (start_cycle == 0) - field |= TRB_CYCLE; - } else - field |= ep_ring->cycle_state; + field ^= trb_cycle_invert; + trb_cycle_invert = 0; /* Chain all the TRBs together; clear the chain bit in the last * TRB to indicate it's the last TRB in the chain. @@ -3330,8 +3322,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } while (running_total < urb->transfer_buffer_length); check_trb_math(urb, num_trbs, running_total); - giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, - start_cycle, start_trb); + giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, start_trb); return 0; } @@ -3344,9 +3335,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct xhci_td *td; int num_trbs; struct xhci_generic_trb *start_trb; - bool first_trb; + u32 trb_cycle_invert; bool more_trbs_coming; - int start_cycle; u32 field, length_field; int running_total, trb_buff_len, ret; @@ -3393,7 +3383,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, * state may change as we enqueue the other TRBs, so save it too. */ start_trb = &ep_ring->enqueue->generic; - start_cycle = ep_ring->cycle_state; running_total = 0; total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length, @@ -3405,20 +3394,17 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (trb_buff_len > urb->transfer_buffer_length) trb_buff_len = urb->transfer_buffer_length; - first_trb = true; + trb_cycle_invert = TRB_CYCLE; /* Queue the first TRB, even if it's zero-length */ do { u32 remainder = 0; field = 0; + field |= ep_ring->cycle_state; /* Don't change the cycle bit of the first TRB until later */ - if (first_trb) { - first_trb = false; - if (start_cycle == 0) - field |= TRB_CYCLE; - } else - field |= ep_ring->cycle_state; + field ^= trb_cycle_invert; + trb_cycle_invert = 0; /* Chain all the TRBs together; clear the chain bit in the last * TRB to indicate it's the last TRB in the chain. @@ -3469,8 +3455,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } while (running_total < urb->transfer_buffer_length); check_trb_math(urb, num_trbs, running_total); - giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, - start_cycle, start_trb); + giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, start_trb); return 0; } @@ -3483,7 +3468,6 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int ret; struct usb_ctrlrequest *setup; struct xhci_generic_trb *start_trb; - int start_cycle; u32 field, length_field; struct urb_priv *urb_priv; struct xhci_td *td; @@ -3523,15 +3507,13 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, * state may change as we enqueue the other TRBs, so save it too. */ start_trb = &ep_ring->enqueue->generic; - start_cycle = ep_ring->cycle_state; /* Queue setup TRB - see section 6.4.1.2.1 */ /* FIXME better way to translate setup_packet into two u32 fields? */ setup = (struct usb_ctrlrequest *) urb->setup_packet; field = 0; field |= TRB_IDT | TRB_TYPE(TRB_SETUP); - if (start_cycle == 0) - field |= TRB_CYCLE; + field |= ep_ring->cycle_state ^ TRB_CYCLE; /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ if (xhci->hci_version == 0x100) { @@ -3586,8 +3568,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Event on completion */ field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state); - giveback_first_trb(xhci, slot_id, ep_index, 0, - start_cycle, start_trb); + giveback_first_trb(xhci, slot_id, ep_index, 0, start_trb); return 0; } @@ -3675,7 +3656,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int num_tds, trbs_per_td; struct xhci_generic_trb *start_trb; bool first_trb; - int start_cycle; u32 field, length_field; int running_total, trb_buff_len, td_len, td_remain_len, ret; u64 start_addr, addr; @@ -3692,7 +3672,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, start_addr = (u64) urb->transfer_dma; start_trb = &ep_ring->enqueue->generic; - start_cycle = ep_ring->cycle_state; urb_priv = urb->hcpriv; /* Queue the first TRB, even if it's zero-length */ @@ -3739,11 +3718,9 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_TYPE(TRB_ISOC); /* Assume URB_ISO_ASAP is set */ field |= TRB_SIA; - if (i == 0) { - if (start_cycle == 0) - field |= TRB_CYCLE; - } else - field |= ep_ring->cycle_state; + field |= ep_ring->cycle_state; + if (i == 0) + field ^= TRB_CYCLE; first_trb = false; } else { /* Queue other normal TRBs */ @@ -3820,8 +3797,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++; - giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, - start_cycle, start_trb); + giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, start_trb); return 0; cleanup: /* Clean up a partially enqueued isoc transfer. */ @@ -3841,7 +3817,9 @@ cleanup: /* Reset the ring enqueue back to the first TRB and its cycle bit. */ ep_ring->enqueue = urb_priv->td[0].first_trb; ep_ring->enq_seg = urb_priv->td[0].start_seg; - ep_ring->cycle_state = start_cycle; + /* The cycle bit in the first TRB won't be modified, get its inverse. */ + ep_ring->cycle_state = (ep_ring->enqueue->generic.field[3] & + TRB_CYCLE) ^ TRB_CYCLE; ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp; usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); return ret; -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html