diff options
Diffstat (limited to 'drivers/usb/dwc2/hcd_intr.c')
-rw-r--r-- | drivers/usb/dwc2/hcd_intr.c | 174 |
1 files changed, 96 insertions, 78 deletions
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index cadba8b13c48..906f223542ee 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c | |||
@@ -55,12 +55,16 @@ | |||
55 | /* This function is for debug only */ | 55 | /* This function is for debug only */ |
56 | static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg) | 56 | static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg) |
57 | { | 57 | { |
58 | #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS | ||
59 | u16 curr_frame_number = hsotg->frame_number; | 58 | u16 curr_frame_number = hsotg->frame_number; |
59 | u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1); | ||
60 | |||
61 | if (expected != curr_frame_number) | ||
62 | dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n", | ||
63 | expected, curr_frame_number); | ||
60 | 64 | ||
65 | #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS | ||
61 | if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) { | 66 | if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) { |
62 | if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) != | 67 | if (expected != curr_frame_number) { |
63 | curr_frame_number) { | ||
64 | hsotg->frame_num_array[hsotg->frame_num_idx] = | 68 | hsotg->frame_num_array[hsotg->frame_num_idx] = |
65 | curr_frame_number; | 69 | curr_frame_number; |
66 | hsotg->last_frame_num_array[hsotg->frame_num_idx] = | 70 | hsotg->last_frame_num_array[hsotg->frame_num_idx] = |
@@ -79,14 +83,15 @@ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg) | |||
79 | } | 83 | } |
80 | hsotg->dumped_frame_num_array = 1; | 84 | hsotg->dumped_frame_num_array = 1; |
81 | } | 85 | } |
82 | hsotg->last_frame_num = curr_frame_number; | ||
83 | #endif | 86 | #endif |
87 | hsotg->last_frame_num = curr_frame_number; | ||
84 | } | 88 | } |
85 | 89 | ||
86 | static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg, | 90 | static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg, |
87 | struct dwc2_host_chan *chan, | 91 | struct dwc2_host_chan *chan, |
88 | struct dwc2_qtd *qtd) | 92 | struct dwc2_qtd *qtd) |
89 | { | 93 | { |
94 | struct usb_device *root_hub = dwc2_hsotg_to_hcd(hsotg)->self.root_hub; | ||
90 | struct urb *usb_urb; | 95 | struct urb *usb_urb; |
91 | 96 | ||
92 | if (!chan->qh) | 97 | if (!chan->qh) |
@@ -102,6 +107,15 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg, | |||
102 | if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt) | 107 | if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt) |
103 | return; | 108 | return; |
104 | 109 | ||
110 | /* | ||
111 | * The root hub doesn't really have a TT, but Linux thinks it | ||
112 | * does because how could you have a "high speed hub" that | ||
113 | * directly talks directly to low speed devices without a TT? | ||
114 | * It's all lies. Lies, I tell you. | ||
115 | */ | ||
116 | if (usb_urb->dev->tt->hub == root_hub) | ||
117 | return; | ||
118 | |||
105 | if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) { | 119 | if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) { |
106 | chan->qh->tt_buffer_dirty = 1; | 120 | chan->qh->tt_buffer_dirty = 1; |
107 | if (usb_hub_clear_tt_buffer(usb_urb)) | 121 | if (usb_hub_clear_tt_buffer(usb_urb)) |
@@ -138,13 +152,19 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg) | |||
138 | while (qh_entry != &hsotg->periodic_sched_inactive) { | 152 | while (qh_entry != &hsotg->periodic_sched_inactive) { |
139 | qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry); | 153 | qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry); |
140 | qh_entry = qh_entry->next; | 154 | qh_entry = qh_entry->next; |
141 | if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) | 155 | if (dwc2_frame_num_le(qh->next_active_frame, |
156 | hsotg->frame_number)) { | ||
157 | dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n", | ||
158 | qh, hsotg->frame_number, | ||
159 | qh->next_active_frame); | ||
160 | |||
142 | /* | 161 | /* |
143 | * Move QH to the ready list to be executed next | 162 | * Move QH to the ready list to be executed next |
144 | * (micro)frame | 163 | * (micro)frame |
145 | */ | 164 | */ |
146 | list_move(&qh->qh_list_entry, | 165 | list_move_tail(&qh->qh_list_entry, |
147 | &hsotg->periodic_sched_ready); | 166 | &hsotg->periodic_sched_ready); |
167 | } | ||
148 | } | 168 | } |
149 | tr_type = dwc2_hcd_select_transactions(hsotg); | 169 | tr_type = dwc2_hcd_select_transactions(hsotg); |
150 | if (tr_type != DWC2_TRANSACTION_NONE) | 170 | if (tr_type != DWC2_TRANSACTION_NONE) |
@@ -472,18 +492,6 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, | |||
472 | xfer_length = urb->length - urb->actual_length; | 492 | xfer_length = urb->length - urb->actual_length; |
473 | } | 493 | } |
474 | 494 | ||
475 | /* Non DWORD-aligned buffer case handling */ | ||
476 | if (chan->align_buf && xfer_length) { | ||
477 | dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); | ||
478 | dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, | ||
479 | chan->qh->dw_align_buf_size, | ||
480 | chan->ep_is_in ? | ||
481 | DMA_FROM_DEVICE : DMA_TO_DEVICE); | ||
482 | if (chan->ep_is_in) | ||
483 | memcpy(urb->buf + urb->actual_length, | ||
484 | chan->qh->dw_align_buf, xfer_length); | ||
485 | } | ||
486 | |||
487 | dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", | 495 | dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", |
488 | urb->actual_length, xfer_length); | 496 | urb->actual_length, xfer_length); |
489 | urb->actual_length += xfer_length; | 497 | urb->actual_length += xfer_length; |
@@ -573,21 +581,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( | |||
573 | frame_desc->status = 0; | 581 | frame_desc->status = 0; |
574 | frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg, | 582 | frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg, |
575 | chan, chnum, qtd, halt_status, NULL); | 583 | chan, chnum, qtd, halt_status, NULL); |
576 | |||
577 | /* Non DWORD-aligned buffer case handling */ | ||
578 | if (chan->align_buf && frame_desc->actual_length) { | ||
579 | dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", | ||
580 | __func__); | ||
581 | dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, | ||
582 | chan->qh->dw_align_buf_size, | ||
583 | chan->ep_is_in ? | ||
584 | DMA_FROM_DEVICE : DMA_TO_DEVICE); | ||
585 | if (chan->ep_is_in) | ||
586 | memcpy(urb->buf + frame_desc->offset + | ||
587 | qtd->isoc_split_offset, | ||
588 | chan->qh->dw_align_buf, | ||
589 | frame_desc->actual_length); | ||
590 | } | ||
591 | break; | 584 | break; |
592 | case DWC2_HC_XFER_FRAME_OVERRUN: | 585 | case DWC2_HC_XFER_FRAME_OVERRUN: |
593 | urb->error_count++; | 586 | urb->error_count++; |
@@ -608,21 +601,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( | |||
608 | frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg, | 601 | frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg, |
609 | chan, chnum, qtd, halt_status, NULL); | 602 | chan, chnum, qtd, halt_status, NULL); |
610 | 603 | ||
611 | /* Non DWORD-aligned buffer case handling */ | ||
612 | if (chan->align_buf && frame_desc->actual_length) { | ||
613 | dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", | ||
614 | __func__); | ||
615 | dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, | ||
616 | chan->qh->dw_align_buf_size, | ||
617 | chan->ep_is_in ? | ||
618 | DMA_FROM_DEVICE : DMA_TO_DEVICE); | ||
619 | if (chan->ep_is_in) | ||
620 | memcpy(urb->buf + frame_desc->offset + | ||
621 | qtd->isoc_split_offset, | ||
622 | chan->qh->dw_align_buf, | ||
623 | frame_desc->actual_length); | ||
624 | } | ||
625 | |||
626 | /* Skip whole frame */ | 604 | /* Skip whole frame */ |
627 | if (chan->qh->do_split && | 605 | if (chan->qh->do_split && |
628 | chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in && | 606 | chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in && |
@@ -688,8 +666,6 @@ static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, | |||
688 | } | 666 | } |
689 | 667 | ||
690 | no_qtd: | 668 | no_qtd: |
691 | if (qh->channel) | ||
692 | qh->channel->align_buf = 0; | ||
693 | qh->channel = NULL; | 669 | qh->channel = NULL; |
694 | dwc2_hcd_qh_deactivate(hsotg, qh, continue_split); | 670 | dwc2_hcd_qh_deactivate(hsotg, qh, continue_split); |
695 | } | 671 | } |
@@ -846,7 +822,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, | |||
846 | * halt to be queued when the periodic schedule is | 822 | * halt to be queued when the periodic schedule is |
847 | * processed. | 823 | * processed. |
848 | */ | 824 | */ |
849 | list_move(&chan->qh->qh_list_entry, | 825 | list_move_tail(&chan->qh->qh_list_entry, |
850 | &hsotg->periodic_sched_assigned); | 826 | &hsotg->periodic_sched_assigned); |
851 | 827 | ||
852 | /* | 828 | /* |
@@ -954,14 +930,6 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, | |||
954 | 930 | ||
955 | frame_desc->actual_length += len; | 931 | frame_desc->actual_length += len; |
956 | 932 | ||
957 | if (chan->align_buf) { | ||
958 | dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); | ||
959 | dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, | ||
960 | chan->qh->dw_align_buf_size, DMA_FROM_DEVICE); | ||
961 | memcpy(qtd->urb->buf + frame_desc->offset + | ||
962 | qtd->isoc_split_offset, chan->qh->dw_align_buf, len); | ||
963 | } | ||
964 | |||
965 | qtd->isoc_split_offset += len; | 933 | qtd->isoc_split_offset += len; |
966 | 934 | ||
967 | if (frame_desc->actual_length >= frame_desc->length) { | 935 | if (frame_desc->actual_length >= frame_desc->length) { |
@@ -1184,19 +1152,6 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, | |||
1184 | xfer_length = urb->length - urb->actual_length; | 1152 | xfer_length = urb->length - urb->actual_length; |
1185 | } | 1153 | } |
1186 | 1154 | ||
1187 | /* Non DWORD-aligned buffer case handling */ | ||
1188 | if (chan->align_buf && xfer_length && chan->ep_is_in) { | ||
1189 | dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); | ||
1190 | dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, | ||
1191 | chan->qh->dw_align_buf_size, | ||
1192 | chan->ep_is_in ? | ||
1193 | DMA_FROM_DEVICE : DMA_TO_DEVICE); | ||
1194 | if (chan->ep_is_in) | ||
1195 | memcpy(urb->buf + urb->actual_length, | ||
1196 | chan->qh->dw_align_buf, | ||
1197 | xfer_length); | ||
1198 | } | ||
1199 | |||
1200 | urb->actual_length += xfer_length; | 1155 | urb->actual_length += xfer_length; |
1201 | 1156 | ||
1202 | hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); | 1157 | hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); |
@@ -1416,14 +1371,50 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg, | |||
1416 | 1371 | ||
1417 | if (chan->ep_type == USB_ENDPOINT_XFER_INT || | 1372 | if (chan->ep_type == USB_ENDPOINT_XFER_INT || |
1418 | chan->ep_type == USB_ENDPOINT_XFER_ISOC) { | 1373 | chan->ep_type == USB_ENDPOINT_XFER_ISOC) { |
1419 | int frnum = dwc2_hcd_get_frame_number(hsotg); | 1374 | struct dwc2_qh *qh = chan->qh; |
1375 | bool past_end; | ||
1376 | |||
1377 | if (hsotg->core_params->uframe_sched <= 0) { | ||
1378 | int frnum = dwc2_hcd_get_frame_number(hsotg); | ||
1379 | |||
1380 | /* Don't have num_hs_transfers; simple logic */ | ||
1381 | past_end = dwc2_full_frame_num(frnum) != | ||
1382 | dwc2_full_frame_num(qh->next_active_frame); | ||
1383 | } else { | ||
1384 | int end_frnum; | ||
1420 | 1385 | ||
1421 | if (dwc2_full_frame_num(frnum) != | ||
1422 | dwc2_full_frame_num(chan->qh->sched_frame)) { | ||
1423 | /* | 1386 | /* |
1424 | * No longer in the same full speed frame. | 1387 | * Figure out the end frame based on schedule. |
1425 | * Treat this as a transaction error. | 1388 | * |
1426 | */ | 1389 | * We don't want to go on trying again and again |
1390 | * forever. Let's stop when we've done all the | ||
1391 | * transfers that were scheduled. | ||
1392 | * | ||
1393 | * We're going to be comparing start_active_frame | ||
1394 | * and next_active_frame, both of which are 1 | ||
1395 | * before the time the packet goes on the wire, | ||
1396 | * so that cancels out. Basically if had 1 | ||
1397 | * transfer and we saw 1 NYET then we're done. | ||
1398 | * We're getting a NYET here so if next >= | ||
1399 | * (start + num_transfers) we're done. The | ||
1400 | * complexity is that for all but ISOC_OUT we | ||
1401 | * skip one slot. | ||
1402 | */ | ||
1403 | end_frnum = dwc2_frame_num_inc( | ||
1404 | qh->start_active_frame, | ||
1405 | qh->num_hs_transfers); | ||
1406 | |||
1407 | if (qh->ep_type != USB_ENDPOINT_XFER_ISOC || | ||
1408 | qh->ep_is_in) | ||
1409 | end_frnum = | ||
1410 | dwc2_frame_num_inc(end_frnum, 1); | ||
1411 | |||
1412 | past_end = dwc2_frame_num_le( | ||
1413 | end_frnum, qh->next_active_frame); | ||
1414 | } | ||
1415 | |||
1416 | if (past_end) { | ||
1417 | /* Treat this as a transaction error. */ | ||
1427 | #if 0 | 1418 | #if 0 |
1428 | /* | 1419 | /* |
1429 | * Todo: Fix system performance so this can | 1420 | * Todo: Fix system performance so this can |
@@ -2008,6 +1999,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) | |||
2008 | } | 1999 | } |
2009 | 2000 | ||
2010 | dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); | 2001 | dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); |
2002 | |||
2003 | /* | ||
2004 | * If we got an interrupt after someone called | ||
2005 | * dwc2_hcd_endpoint_disable() we don't want to crash below | ||
2006 | */ | ||
2007 | if (!chan->qh) { | ||
2008 | dev_warn(hsotg->dev, "Interrupt on disabled channel\n"); | ||
2009 | return; | ||
2010 | } | ||
2011 | |||
2011 | chan->hcint = hcint; | 2012 | chan->hcint = hcint; |
2012 | hcint &= hcintmsk; | 2013 | hcint &= hcintmsk; |
2013 | 2014 | ||
@@ -2130,6 +2131,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg) | |||
2130 | { | 2131 | { |
2131 | u32 haint; | 2132 | u32 haint; |
2132 | int i; | 2133 | int i; |
2134 | struct dwc2_host_chan *chan, *chan_tmp; | ||
2133 | 2135 | ||
2134 | haint = dwc2_readl(hsotg->regs + HAINT); | 2136 | haint = dwc2_readl(hsotg->regs + HAINT); |
2135 | if (dbg_perio()) { | 2137 | if (dbg_perio()) { |
@@ -2138,6 +2140,22 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg) | |||
2138 | dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint); | 2140 | dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint); |
2139 | } | 2141 | } |
2140 | 2142 | ||
2143 | /* | ||
2144 | * According to USB 2.0 spec section 11.18.8, a host must | ||
2145 | * issue complete-split transactions in a microframe for a | ||
2146 | * set of full-/low-speed endpoints in the same relative | ||
2147 | * order as the start-splits were issued in a microframe for. | ||
2148 | */ | ||
2149 | list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order, | ||
2150 | split_order_list_entry) { | ||
2151 | int hc_num = chan->hc_num; | ||
2152 | |||
2153 | if (haint & (1 << hc_num)) { | ||
2154 | dwc2_hc_n_intr(hsotg, hc_num); | ||
2155 | haint &= ~(1 << hc_num); | ||
2156 | } | ||
2157 | } | ||
2158 | |||
2141 | for (i = 0; i < hsotg->core_params->host_channels; i++) { | 2159 | for (i = 0; i < hsotg->core_params->host_channels; i++) { |
2142 | if (haint & (1 << i)) | 2160 | if (haint & (1 << i)) |
2143 | dwc2_hc_n_intr(hsotg, i); | 2161 | dwc2_hc_n_intr(hsotg, i); |