Date: Sat, 7 Jun 2014 07:23:18 +0000 (UTC) From: Hans Petter Selasky <hselasky@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r267210 - head/sys/dev/usb/controller Message-ID: <201406070723.s577NIod056025@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: hselasky Date: Sat Jun 7 07:23:17 2014 New Revision: 267210 URL: http://svnweb.freebsd.org/changeset/base/267210 Log: Some further DWC OTG improvements for full speed and low speed devices: - Revert r265427. It appears we are halting the DWC OTG host controller schedule if we process events only at every SOF. When doing split transactions we rely on that events are processed quickly and waiting too long might cause data loss. - We are not always able to meet the timing requirements of interrupt endpoint split transactions. Switch from INTERRUPT to CONTROL endpoint type for interrupt endpoint events until further, hence CONTROL endpoint events are more relaxed, reducing the chance of data loss. See comment in code for more in-depth explanation. - Simplify TT scheduling. MFC after: 3 days Modified: head/sys/dev/usb/controller/dwc_otg.c head/sys/dev/usb/controller/dwc_otg.h Modified: head/sys/dev/usb/controller/dwc_otg.c ============================================================================== --- head/sys/dev/usb/controller/dwc_otg.c Sat Jun 7 05:14:07 2014 (r267209) +++ head/sys/dev/usb/controller/dwc_otg.c Sat Jun 7 07:23:17 2014 (r267210) @@ -98,6 +98,8 @@ GINTMSK_USBSUSPMSK | \ GINTMSK_IEPINTMSK | \ GINTMSK_SESSREQINTMSK | \ + GINTMSK_RXFLVLMSK | \ + GINTMSK_HCHINTMSK | \ GINTMSK_OTGINTMSK | \ GINTMSK_PRTINTMSK) @@ -239,7 +241,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc * for (x = 0; x != sc->sc_host_ch_max; x++) { /* disable all host interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0); + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), + HCINT_DEFAULT_MASK); } DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ, @@ -256,8 +259,9 @@ dwc_otg_init_fifo(struct dwc_otg_softc * /* store maximum periodic and non-periodic FIFO TX size */ sc->sc_tx_max_size = fifo_size; - /* disable all host channel interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 0); + /* enable all host channel interrupts */ + DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, + (1U << sc->sc_host_ch_max) - 1U); } if (mode == DWC_MODE_DEVICE) { @@ -568,11 +572,9 @@ dwc_otg_common_rx_ack(struct dwc_otg_sof { DPRINTFN(5, "RX status clear\n"); - if (sc->sc_flags.status_device_mode != 0) { - /* enable RX FIFO level interrupt */ - sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); - } + /* enable RX FIFO level interrupt */ + sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; + DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* clear cached status */ sc->sc_last_rx_status = 0; @@ -607,8 +609,7 @@ dwc_otg_host_channel_alloc(struct dwc_ot /* compute needed TX FIFO size */ if (is_out != 0) { - if (td->ep_type == UE_INTERRUPT || - td->ep_type == UE_ISOCHRONOUS) { + if (td->ep_type == UE_ISOCHRONOUS) { tx_p_size = td->max_packet_size; tx_np_size = 0; if (td->hcsplt != 0 && tx_p_size > HCSPLT_XACTLEN_BURST) @@ -687,7 +688,7 @@ dwc_otg_host_channel_free(struct dwc_otg * after a fixed given amount of time the host channel is no * longer doing any USB traffic: */ - if (td->ep_type == UE_ISOCHRONOUS || td->ep_type == UE_INTERRUPT) { + if (td->ep_type == UE_ISOCHRONOUS) { /* double buffered */ sc->sc_chan_state[x].wait_sof = DWC_OTG_SLOT_IDLE_MAX; } else { @@ -874,10 +875,7 @@ send_pkt: DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4); /* wait until next slot before trying complete split */ - if (td->ep_type == UE_INTERRUPT || td->ep_type == UE_ISOCHRONOUS) - td->tt_complete_slot = sc->sc_last_frame_num + 2; - else - td->tt_complete_slot = sc->sc_last_frame_num + 1; + td->tt_complete_slot = sc->sc_last_frame_num + 1; /* store number of bytes transmitted */ td->tx_bytes = sizeof(req); @@ -1352,15 +1350,7 @@ receive_pkt: } delta = sc->sc_last_frame_num - td->tt_start_slot; if (delta > DWC_OTG_TT_SLOT_MAX) { - if (td->ep_type == UE_INTERRUPT) { - /* - * Happens from time to time avoid - * posting an error, instead retry - * the start split packet: - */ - td->tt_scheduled = 0; - goto receive_spkt; - } else if (td->ep_type != UE_ISOCHRONOUS) { + if (td->ep_type != UE_ISOCHRONOUS) { /* we missed the service interval */ td->error_any = 1; } @@ -1460,10 +1450,7 @@ receive_spkt: hcchar |= HCCHAR_EPDIR_IN; /* wait until next slot before trying complete split */ - if (td->ep_type == UE_INTERRUPT || td->ep_type == UE_ISOCHRONOUS) - td->tt_complete_slot = sc->sc_last_frame_num + 2; - else - td->tt_complete_slot = sc->sc_last_frame_num + 1; + td->tt_complete_slot = sc->sc_last_frame_num + 1; /* must enable channel before data can be received */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar); @@ -2346,18 +2333,11 @@ dwc_otg_host_channel_disable(struct dwc_ } static uint16_t -dwc_otg_compute_tt_slot(struct dwc_otg_tt_info *pinfo, uint16_t io_bytes) +dwc_otg_compute_isoc_rx_tt_slot(struct dwc_otg_tt_info *pinfo) { - const uint16_t limit = (188 * 5) / 6; /* includes bit-stuffing */ - uint16_t retval = pinfo->slot_index; - - pinfo->bytes_used += io_bytes; - while (pinfo->slot_index < 6 && - pinfo->bytes_used >= limit) { - pinfo->bytes_used -= limit; + if (pinfo->slot_index < DWC_OTG_TT_SLOT_MAX) pinfo->slot_index++; - } - return (retval); + return (pinfo->slot_index); } static uint8_t @@ -2412,11 +2392,17 @@ dwc_otg_update_host_transfer_schedule_lo continue; /* compute slot */ - slot = temp + dwc_otg_compute_tt_slot( - sc->sc_tt_info + td->tt_index, td->remainder); - - /* Start ASAP */ - td->tt_start_slot = slot + 0; + slot = dwc_otg_compute_isoc_rx_tt_slot( + sc->sc_tt_info + td->tt_index); + if (slot > 3) { + /* + * Not enough time to get complete + * split executed. + */ + continue; + } + /* Delayed start */ + td->tt_start_slot = temp + slot; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); @@ -2439,12 +2425,8 @@ dwc_otg_update_host_transfer_schedule_lo if (td->hcsplt == 0 || td->tt_scheduled != 0) continue; - /* compute slot */ - slot = temp + dwc_otg_compute_tt_slot( - sc->sc_tt_info + td->tt_index, td->remainder); - /* Start ASAP */ - td->tt_start_slot = slot + 0; + td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); @@ -2469,12 +2451,8 @@ dwc_otg_update_host_transfer_schedule_lo continue; } - /* compute slot */ - slot = temp + dwc_otg_compute_tt_slot(sc->sc_tt_info + td->tt_index, - td->max_packet_size); - /* start ASAP */ - td->tt_start_slot = slot + 0; + td->tt_start_slot = temp; sc->sc_needsof = 1; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); @@ -2494,11 +2472,8 @@ dwc_otg_update_host_transfer_schedule_lo if (td->hcsplt == 0 || td->tt_scheduled != 0) continue; - /* compute slot */ - slot = temp + dwc_otg_compute_tt_slot(sc->sc_tt_info + td->tt_index, 0); - /* start ASAP */ - td->tt_start_slot = slot + 0; + td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); @@ -2518,17 +2493,8 @@ dwc_otg_update_host_transfer_schedule_lo if (td->hcsplt == 0 || td->tt_scheduled != 0) continue; - /* compute slot */ - slot = dwc_otg_compute_tt_slot(sc->sc_tt_info + td->tt_index, 0); - - /* figure out highest slot number */ - if (slot < (temp & 7)) - slot = temp; - else - slot += (temp & ~7); - /* start ASAP */ - td->tt_start_slot = slot + 0; + td->tt_start_slot = temp; td->tt_scheduled = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); @@ -2809,8 +2775,6 @@ dwc_otg_interrupt(void *arg) /* Disable SOF interrupt */ sc->sc_irq_mask &= ~GINTMSK_SOFMSK; - /* Enable RX frame interrupt */ - sc->sc_irq_mask |= GINTMSK_RXFLVLMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* complete root HUB interrupt endpoint */ @@ -2851,7 +2815,7 @@ dwc_otg_interrupt(void *arg) * suspend and RX frame interrupt: */ sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK); - sc->sc_irq_mask |= (GINTMSK_USBSUSPMSK | GINTMSK_RXFLVLMSK); + sc->sc_irq_mask |= GINTMSK_USBSUSPMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* complete root HUB interrupt endpoint */ @@ -2922,10 +2886,6 @@ dwc_otg_interrupt(void *arg) /* complete root HUB interrupt endpoint */ dwc_otg_root_intr(sc); - /* disable RX FIFO level interrupt */ - sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); - /* update host frame interval */ dwc_otg_update_host_frame_interval(sc); } @@ -3222,9 +3182,26 @@ dwc_otg_setup_standard_chain(struct usb_ (xfer->address << HCCHAR_DEVADDR_SHIFT) | ((xfer->endpointno & UE_ADDR) << HCCHAR_EPNUM_SHIFT) | (xfer->max_packet_size << HCCHAR_MPS_SHIFT) | - (td->ep_type << HCCHAR_EPTYPE_SHIFT) | HCCHAR_CHENA; + /* + * We are not always able to meet the timing + * requirements of the USB interrupt endpoint's + * complete split token, when doing transfers going + * via a transaction translator. Use the CONTROL + * transfer type instead of the INTERRUPT transfer + * type in general, as a means to workaround + * that. This trick should work for both FULL and LOW + * speed USB traffic going through a TT. For non-TT + * traffic it works aswell. The reason for using + * CONTROL type instead of BULK is that some TTs might + * reject LOW speed BULK traffic. + */ + if (td->ep_type == UE_INTERRUPT) + hcchar |= (UE_CONTROL << HCCHAR_EPTYPE_SHIFT); + else + hcchar |= (td->ep_type << HCCHAR_EPTYPE_SHIFT); + if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_LOW) hcchar |= HCCHAR_LSPDDEV; if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) @@ -3262,13 +3239,12 @@ dwc_otg_setup_standard_chain(struct usb_ break; case USB_SPEED_HIGH: hcsplt = 0; - if (td->ep_type == UE_ISOCHRONOUS || - td->ep_type == UE_INTERRUPT) { - hcchar |= ((xfer->max_packet_count & 3) - << HCCHAR_MC_SHIFT); - } if (td->ep_type == UE_INTERRUPT) { uint32_t ival; +#if 0 + hcchar |= ((xfer->max_packet_count & 3) + << HCCHAR_MC_SHIFT); +#endif ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE; if (ival == 0) ival = 1; @@ -3277,6 +3253,8 @@ dwc_otg_setup_standard_chain(struct usb_ td->tmr_val = sc->sc_tmr_val + ival; td->tmr_res = ival; } else if (td->ep_type == UE_ISOCHRONOUS) { + hcchar |= ((xfer->max_packet_count & 3) + << HCCHAR_MC_SHIFT); td->tmr_val = 0; td->tmr_res = 1 << usbd_xfer_get_fps_shift(xfer); } else { Modified: head/sys/dev/usb/controller/dwc_otg.h ============================================================================== --- head/sys/dev/usb/controller/dwc_otg.h Sat Jun 7 05:14:07 2014 (r267209) +++ head/sys/dev/usb/controller/dwc_otg.h Sat Jun 7 07:23:17 2014 (r267210) @@ -35,7 +35,7 @@ #define DWC_OTG_MAX_ENDPOINTS 16 #define DWC_OTG_HOST_TIMER_RATE 10 /* ms */ #define DWC_OTG_TT_SLOT_MAX 8 -#define DWC_OTG_SLOT_IDLE_MAX 4 +#define DWC_OTG_SLOT_IDLE_MAX 3 #define DWC_OTG_SLOT_IDLE_MIN 2 #define DWC_OTG_NAK_MAX 8 /* 1 ms */ @@ -93,9 +93,7 @@ struct dwc_otg_td { }; struct dwc_otg_tt_info { - uint16_t bytes_used; uint8_t slot_index; - uint8_t dummy; }; struct dwc_otg_std_temp {
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201406070723.s577NIod056025>