diff options
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 44 |
1 files changed, 35 insertions, 9 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 48e60d166ff0..b18e00ecb468 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c | |||
@@ -68,6 +68,10 @@ | |||
68 | #include <linux/slab.h> | 68 | #include <linux/slab.h> |
69 | #include "xhci.h" | 69 | #include "xhci.h" |
70 | 70 | ||
71 | static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, | ||
72 | struct xhci_virt_device *virt_dev, | ||
73 | struct xhci_event_cmd *event); | ||
74 | |||
71 | /* | 75 | /* |
72 | * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA | 76 | * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA |
73 | * address of the TRB. | 77 | * address of the TRB. |
@@ -313,7 +317,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) | |||
313 | xhci_readl(xhci, &xhci->dba->doorbell[0]); | 317 | xhci_readl(xhci, &xhci->dba->doorbell[0]); |
314 | } | 318 | } |
315 | 319 | ||
316 | static void ring_ep_doorbell(struct xhci_hcd *xhci, | 320 | void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, |
317 | unsigned int slot_id, | 321 | unsigned int slot_id, |
318 | unsigned int ep_index, | 322 | unsigned int ep_index, |
319 | unsigned int stream_id) | 323 | unsigned int stream_id) |
@@ -353,7 +357,7 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, | |||
353 | /* A ring has pending URBs if its TD list is not empty */ | 357 | /* A ring has pending URBs if its TD list is not empty */ |
354 | if (!(ep->ep_state & EP_HAS_STREAMS)) { | 358 | if (!(ep->ep_state & EP_HAS_STREAMS)) { |
355 | if (!(list_empty(&ep->ring->td_list))) | 359 | if (!(list_empty(&ep->ring->td_list))) |
356 | ring_ep_doorbell(xhci, slot_id, ep_index, 0); | 360 | xhci_ring_ep_doorbell(xhci, slot_id, ep_index, 0); |
357 | return; | 361 | return; |
358 | } | 362 | } |
359 | 363 | ||
@@ -361,7 +365,8 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, | |||
361 | stream_id++) { | 365 | stream_id++) { |
362 | struct xhci_stream_info *stream_info = ep->stream_info; | 366 | struct xhci_stream_info *stream_info = ep->stream_info; |
363 | if (!list_empty(&stream_info->stream_rings[stream_id]->td_list)) | 367 | if (!list_empty(&stream_info->stream_rings[stream_id]->td_list)) |
364 | ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); | 368 | xhci_ring_ep_doorbell(xhci, slot_id, ep_index, |
369 | stream_id); | ||
365 | } | 370 | } |
366 | } | 371 | } |
367 | 372 | ||
@@ -626,10 +631,11 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, | |||
626 | * bit cleared) so that the HW will skip over them. | 631 | * bit cleared) so that the HW will skip over them. |
627 | */ | 632 | */ |
628 | static void handle_stopped_endpoint(struct xhci_hcd *xhci, | 633 | static void handle_stopped_endpoint(struct xhci_hcd *xhci, |
629 | union xhci_trb *trb) | 634 | union xhci_trb *trb, struct xhci_event_cmd *event) |
630 | { | 635 | { |
631 | unsigned int slot_id; | 636 | unsigned int slot_id; |
632 | unsigned int ep_index; | 637 | unsigned int ep_index; |
638 | struct xhci_virt_device *virt_dev; | ||
633 | struct xhci_ring *ep_ring; | 639 | struct xhci_ring *ep_ring; |
634 | struct xhci_virt_ep *ep; | 640 | struct xhci_virt_ep *ep; |
635 | struct list_head *entry; | 641 | struct list_head *entry; |
@@ -638,6 +644,21 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, | |||
638 | 644 | ||
639 | struct xhci_dequeue_state deq_state; | 645 | struct xhci_dequeue_state deq_state; |
640 | 646 | ||
647 | if (unlikely(TRB_TO_SUSPEND_PORT( | ||
648 | xhci->cmd_ring->dequeue->generic.field[3]))) { | ||
649 | slot_id = TRB_TO_SLOT_ID( | ||
650 | xhci->cmd_ring->dequeue->generic.field[3]); | ||
651 | virt_dev = xhci->devs[slot_id]; | ||
652 | if (virt_dev) | ||
653 | handle_cmd_in_cmd_wait_list(xhci, virt_dev, | ||
654 | event); | ||
655 | else | ||
656 | xhci_warn(xhci, "Stop endpoint command " | ||
657 | "completion for disabled slot %u\n", | ||
658 | slot_id); | ||
659 | return; | ||
660 | } | ||
661 | |||
641 | memset(&deq_state, 0, sizeof(deq_state)); | 662 | memset(&deq_state, 0, sizeof(deq_state)); |
642 | slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); | 663 | slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); |
643 | ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); | 664 | ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); |
@@ -1091,7 +1112,7 @@ bandwidth_change: | |||
1091 | complete(&xhci->addr_dev); | 1112 | complete(&xhci->addr_dev); |
1092 | break; | 1113 | break; |
1093 | case TRB_TYPE(TRB_STOP_RING): | 1114 | case TRB_TYPE(TRB_STOP_RING): |
1094 | handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue); | 1115 | handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event); |
1095 | break; | 1116 | break; |
1096 | case TRB_TYPE(TRB_SET_DEQ): | 1117 | case TRB_TYPE(TRB_SET_DEQ): |
1097 | handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue); | 1118 | handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue); |
@@ -2347,7 +2368,7 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, | |||
2347 | */ | 2368 | */ |
2348 | wmb(); | 2369 | wmb(); |
2349 | start_trb->field[3] |= start_cycle; | 2370 | start_trb->field[3] |= start_cycle; |
2350 | ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); | 2371 | xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); |
2351 | } | 2372 | } |
2352 | 2373 | ||
2353 | /* | 2374 | /* |
@@ -2931,7 +2952,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, | |||
2931 | wmb(); | 2952 | wmb(); |
2932 | start_trb->field[3] |= start_cycle; | 2953 | start_trb->field[3] |= start_cycle; |
2933 | 2954 | ||
2934 | ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id); | 2955 | xhci_ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id); |
2935 | return 0; | 2956 | return 0; |
2936 | } | 2957 | } |
2937 | 2958 | ||
@@ -3108,15 +3129,20 @@ int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, | |||
3108 | false); | 3129 | false); |
3109 | } | 3130 | } |
3110 | 3131 | ||
3132 | /* | ||
3133 | * Suspend is set to indicate "Stop Endpoint Command" is being issued to stop | ||
3134 | * activity on an endpoint that is about to be suspended. | ||
3135 | */ | ||
3111 | int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id, | 3136 | int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id, |
3112 | unsigned int ep_index) | 3137 | unsigned int ep_index, int suspend) |
3113 | { | 3138 | { |
3114 | u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); | 3139 | u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); |
3115 | u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); | 3140 | u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); |
3116 | u32 type = TRB_TYPE(TRB_STOP_RING); | 3141 | u32 type = TRB_TYPE(TRB_STOP_RING); |
3142 | u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend); | ||
3117 | 3143 | ||
3118 | return queue_command(xhci, 0, 0, 0, | 3144 | return queue_command(xhci, 0, 0, 0, |
3119 | trb_slot_id | trb_ep_index | type, false); | 3145 | trb_slot_id | trb_ep_index | type | trb_suspend, false); |
3120 | } | 3146 | } |
3121 | 3147 | ||
3122 | /* Set Transfer Ring Dequeue Pointer command. | 3148 | /* Set Transfer Ring Dequeue Pointer command. |