Date: Tue, 6 May 2014 09:12:32 +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: r265427 - head/sys/dev/usb/controller Message-ID: <201405060912.s469CWnZ097391@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: hselasky Date: Tue May 6 09:12:32 2014 New Revision: 265427 URL: http://svnweb.freebsd.org/changeset/base/265427 Log: Reduce the number of interrupts in USB host mode for the DWC OTG controller driver by piggybacking the SOF interrupt when issuing new and checking old transfers. Number of interrupts was reduced by 30% when doing Isochronous transfers. Use correct GINTMSK_XXX macros when accessing the DWC OTG interrupt mask register. Add code to adjust the frame interval register which influences the SOF rate. MFC after: 2 weeks Modified: head/sys/dev/usb/controller/dwc_otg.c Modified: head/sys/dev/usb/controller/dwc_otg.c ============================================================================== --- head/sys/dev/usb/controller/dwc_otg.c Tue May 6 07:21:50 2014 (r265426) +++ head/sys/dev/usb/controller/dwc_otg.c Tue May 6 09:12:32 2014 (r265427) @@ -93,15 +93,13 @@ DWC_OTG_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus) #define DWC_OTG_MSK_GINT_ENABLED \ - (GINTSTS_ENUMDONE | \ - GINTSTS_USBRST | \ - GINTSTS_USBSUSP | \ - GINTSTS_IEPINT | \ - GINTSTS_RXFLVL | \ - GINTSTS_SESSREQINT | \ + (GINTMSK_ENUMDONEMSK | \ + GINTMSK_USBRSTMSK | \ + GINTMSK_USBSUSPMSK | \ + GINTMSK_IEPINTMSK | \ + GINTMSK_SESSREQINTMSK | \ GINTMSK_OTGINTMSK | \ - GINTMSK_HCHINTMSK | \ - GINTSTS_PRTINT) + GINTMSK_PRTINTMSK) static int dwc_otg_use_hsic; @@ -222,8 +220,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc * tx_start += fifo_size; for (x = 0; x != sc->sc_host_ch_max; x++) { - /* enable all needed interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), HCINT_DEFAULT_MASK); + /* disable all host interrupts */ + DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(x), 0); } DWC_OTG_WRITE_4(sc, DOTG_HPTXFSIZ, @@ -233,9 +231,8 @@ dwc_otg_init_fifo(struct dwc_otg_softc * /* store maximum TX FIFO size */ sc->sc_tx_max_size = fifo_size; - /* enable host channel interrupts */ - DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, - (1U << sc->sc_host_ch_max) - 1U); + /* disable all host channel interrupts */ + DWC_OTG_WRITE_4(sc, DOTG_HAINTMSK, 0); } if (mode == DWC_MODE_DEVICE) { @@ -324,6 +321,36 @@ dwc_otg_init_fifo(struct dwc_otg_softc * } static void +dwc_otg_update_host_frame_interval(struct dwc_otg_softc *sc) +{ + uint32_t temp; + + /* setup HOST frame interval register, based on existing value */ + temp = DWC_OTG_READ_4(sc, DOTG_HFIR) & HFIR_FRINT_MASK; + if (temp >= 10000) + temp /= 1000; + else + temp /= 125; + + /* figure out nearest X-tal value */ + if (temp >= 54) + temp = 60; /* MHz */ + else if (temp >= 39) + temp = 48; /* MHz */ + else + temp = 30; /* MHz */ + + if (sc->sc_flags.status_high_speed) + temp *= 125; + else + temp *= 1000; + + DPRINTF("HFIR=0x%08x\n", temp); + + DWC_OTG_WRITE_4(sc, DOTG_HFIR, temp); +} + +static void dwc_otg_clocks_on(struct dwc_otg_softc *sc) { if (sc->sc_flags.clocks_off && @@ -386,7 +413,9 @@ dwc_otg_pull_down(struct dwc_otg_softc * static void dwc_otg_enable_sof_irq(struct dwc_otg_softc *sc) { - if (sc->sc_irq_mask & GINTMSK_SOFMSK) + /* In device mode we don't use the SOF interrupt */ + if (sc->sc_flags.status_device_mode != 0 || + (sc->sc_irq_mask & GINTMSK_SOFMSK) != 0) return; sc->sc_irq_mask |= GINTMSK_SOFMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); @@ -405,8 +434,8 @@ dwc_otg_resume_irq(struct dwc_otg_softc * Disable resume interrupt and enable suspend * interrupt: */ - sc->sc_irq_mask &= ~GINTSTS_WKUPINT; - sc->sc_irq_mask |= GINTSTS_USBSUSP; + sc->sc_irq_mask &= ~GINTMSK_WKUPINTMSK; + sc->sc_irq_mask |= GINTMSK_USBSUSPMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } @@ -428,8 +457,8 @@ dwc_otg_suspend_irq(struct dwc_otg_softc * Disable suspend interrupt and enable resume * interrupt: */ - sc->sc_irq_mask &= ~GINTSTS_USBSUSP; - sc->sc_irq_mask |= GINTSTS_WKUPINT; + sc->sc_irq_mask &= ~GINTMSK_USBSUSPMSK; + sc->sc_irq_mask |= GINTMSK_WKUPINTMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } @@ -503,9 +532,11 @@ dwc_otg_common_rx_ack(struct dwc_otg_sof { DPRINTFN(5, "RX status clear\n"); - /* enable RX FIFO level interrupt */ - sc->sc_irq_mask |= GINTSTS_RXFLVL; - DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); + 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); + } /* clear cached status */ sc->sc_last_rx_status = 0; @@ -674,8 +705,6 @@ dwc_otg_host_channel_disable(struct dwc_ HCCHAR_CHENA | HCCHAR_CHDIS); /* don't re-use channel until next SOF is transmitted */ sc->sc_chan_state[x].wait_sof = 2; - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); } } @@ -724,7 +753,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td uint32_t hcchar; if (dwc_otg_host_channel_alloc(td)) - return (1); /* busy */ + goto busy; /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); @@ -743,13 +772,13 @@ dwc_otg_host_setup_tx(struct dwc_otg_td DPRINTF("CH=%d STALL\n", td->channel); td->error_stall = 1; td->error_any = 1; - return (0); /* complete */ + goto complete; } else if (hcint & HCINT_ERRORS) { DPRINTF("CH=%d ERROR\n", td->channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { td->error_any = 1; - return (0); /* complete */ + goto complete; } } @@ -785,7 +814,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td td->remainder -= td->tx_bytes; td->toggle = 1; td->tt_scheduled = 0; - return (0); /* complete */ + goto complete; } break; @@ -823,7 +852,7 @@ dwc_otg_host_setup_tx(struct dwc_otg_td td->offset += td->tx_bytes; td->remainder -= td->tx_bytes; td->toggle = 1; - return (0); /* complete */ + goto complete; } break; @@ -835,12 +864,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_td default: break; } - return (1); /* busy */ + goto busy; send_pkt: if (sizeof(req) != td->remainder) { td->error_any = 1; - return (0); /* complete */ + goto complete; } if (td->hcsplt != 0) { @@ -880,7 +909,7 @@ send_pkt: /* store number of bytes transmitted */ td->tx_bytes = sizeof(req); - return (1); /* busy */ + goto busy; send_cpkt: /* Wait for our turn, if TT transfer */ @@ -906,15 +935,15 @@ send_cpkt: /* must enable channel before writing data to FIFO */ DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar); - return (1); /* busy */ + goto busy; tt_wait: - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - /* free allocated channel */ dwc_otg_host_channel_free(td); +busy: return (1); /* busy */ +complete: + return (0); /* complete */ } static uint8_t @@ -1088,16 +1117,15 @@ dwc_otg_host_rate_check(struct dwc_otg_t /* non TT isochronous traffic */ if ((td->tmr_val != 0) || (sc->sc_last_frame_num & (td->tmr_res - 1))) { - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); goto busy; } td->tmr_val = 1; /* executed */ td->toggle = 0; } else if (ep_type == UE_INTERRUPT) { - /* non TT interrupt traffic */ - return (dwc_otg_host_rate_check_interrupt(sc, td)); + if (!td->tt_scheduled) + goto busy; + td->tt_scheduled = 0; } else if (td->did_nak != 0) { goto busy; } else if (td->set_toggle) { @@ -1119,7 +1147,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td * uint8_t ep_type; if (dwc_otg_host_channel_alloc(td)) - return (1); /* busy */ + goto busy; /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); @@ -1143,14 +1171,14 @@ dwc_otg_host_data_rx(struct dwc_otg_td * DPRINTF("CH=%d STALL\n", td->channel); td->error_stall = 1; td->error_any = 1; - return (0); /* complete */ + goto complete; } else if (hcint & HCINT_ERRORS) { DPRINTF("CH=%d ERROR\n", td->channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { if (ep_type != UE_ISOCHRONOUS) { td->error_any = 1; - return (0); /* complete */ + goto complete; } } } @@ -1219,7 +1247,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td * /* release FIFO */ dwc_otg_common_rx_ack(sc); - return (0); /* we are complete */ + goto complete; } } td->toggle ^= 1; @@ -1233,7 +1261,7 @@ dwc_otg_host_data_rx(struct dwc_otg_td * /* release FIFO */ dwc_otg_common_rx_ack(sc); - return (0); /* we are complete */ + goto complete; } usbd_copy_in(td->pc, td->offset, @@ -1277,7 +1305,7 @@ check_state: if (hcint & HCINT_NYET) { if (ep_type == UE_ISOCHRONOUS) { /* we missed the service interval */ - return (0); /* complete */ + goto complete; } if (!dwc_otg_host_channel_wait(td)) break; @@ -1293,14 +1321,14 @@ check_state: /* check if we are complete */ if ((td->remainder == 0) || (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) - return (0); /* complete */ + goto complete; goto receive_pkt; } else { /* check if we are complete */ if ((td->remainder == 0) || (td->got_short != 0)) { if (td->short_pkt) - return (0); /* complete */ + goto complete; /* * Else need to receive a zero length @@ -1439,13 +1467,12 @@ receive_spkt: goto busy; tt_wait: - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - /* free allocated channel */ dwc_otg_host_channel_free(td); busy: return (1); /* busy */ +complete: + return (0); /* complete */ } static uint8_t @@ -1571,7 +1598,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * uint8_t ep_type; if (dwc_otg_host_channel_alloc(td)) - return (1); /* busy */ + goto busy; /* get pointer to softc */ sc = DWC_OTG_PC2SC(td->pc); @@ -1593,13 +1620,13 @@ dwc_otg_host_data_tx(struct dwc_otg_td * DPRINTF("CH=%d STALL\n", td->channel); td->error_stall = 1; td->error_any = 1; - return (0); /* complete */ + goto complete; } else if (hcint & HCINT_ERRORS) { DPRINTF("CH=%d ERROR\n", td->channel); td->errcnt++; if (td->hcsplt != 0 || td->errcnt >= 3) { td->error_any = 1; - return (0); /* complete */ + goto complete; } } @@ -1640,7 +1667,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) - return (0); /* complete */ + goto complete; /* * Else we need to transmit a short @@ -1690,7 +1717,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) - return (0); /* complete */ + goto complete; /* else we need to transmit a short packet */ } @@ -1723,7 +1750,7 @@ dwc_otg_host_data_tx(struct dwc_otg_td * dwc_otg_host_channel_disable(sc, td->channel); if (td->remainder == 0) - return (0); /* complete */ + goto complete; td->state = DWC_CHAN_ST_TX_PKT_ISOC; @@ -1949,13 +1976,12 @@ send_cpkt: goto busy; tt_wait: - /* enable SOF interrupt */ - dwc_otg_enable_sof_irq(sc); - /* free allocated channel */ dwc_otg_host_channel_free(td); busy: return (1); /* busy */ +complete: + return (0); /* complete */ } static uint8_t @@ -2280,8 +2306,8 @@ dwc_otg_timer(void *_sc) td->did_nak = 0; } - /* poll jobs */ - dwc_otg_interrupt_poll(sc); + /* enable SOF interrupt, which will poll jobs */ + dwc_otg_enable_sof_irq(sc); if (sc->sc_timer_active) { /* restart timer */ @@ -2318,7 +2344,7 @@ dwc_otg_timer_stop(struct dwc_otg_softc } static void -dwc_otg_update_host_transfer_state(struct dwc_otg_softc *sc) +dwc_otg_update_host_transfer_schedule(struct dwc_otg_softc *sc) { TAILQ_HEAD(, usb_xfer) head; struct usb_xfer *xfer; @@ -2364,11 +2390,16 @@ dwc_otg_update_host_transfer_state(struc struct dwc_otg_tt_info *pinfo; td = xfer->td_transfer_cache; - if (td == NULL || td->hcsplt == 0 || + if (td == NULL || td->did_nak != 0 || (td->hcchar & HCCHAR_EPTYPE_MASK) != (UE_CONTROL << HCCHAR_EPTYPE_SHIFT)) continue; + needsof = 1; + + if (td->hcsplt == 0) + continue; + /* Reset state if stuck waiting for complete split */ if (td->state == DWC_CHAN_ST_WAIT_C_PKT) td->state = DWC_CHAN_ST_START; @@ -2385,7 +2416,6 @@ dwc_otg_update_host_transfer_state(struc td->tt_complete_slot = pinfo->slot_index + 2; if (td->tt_complete_slot < 8) { td->tt_scheduled = 1; - needsof = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } else { @@ -2437,7 +2467,7 @@ dwc_otg_update_host_transfer_state(struc struct dwc_otg_tt_info *pinfo; td = xfer->td_transfer_cache; - if (td == NULL || td->hcsplt == 0 || + if (td == NULL || (td->hcchar & HCCHAR_EPTYPE_MASK) != (UE_INTERRUPT << HCCHAR_EPTYPE_SHIFT)) { continue; @@ -2446,6 +2476,13 @@ dwc_otg_update_host_transfer_state(struc if (dwc_otg_host_rate_check_interrupt(sc, td)) continue; + needsof = 1; + + if (td->hcsplt == 0) { + td->tt_scheduled = 1; + continue; + } + /* Reset state if stuck waiting for complete split */ if (td->state == DWC_CHAN_ST_WAIT_C_PKT) td->state = DWC_CHAN_ST_START; @@ -2462,7 +2499,6 @@ dwc_otg_update_host_transfer_state(struc td->tt_complete_slot = pinfo->slot_index + 2; if (td->tt_complete_slot < 8) { td->tt_scheduled = 1; - needsof = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } else { @@ -2476,12 +2512,17 @@ dwc_otg_update_host_transfer_state(struc struct dwc_otg_tt_info *pinfo; td = xfer->td_transfer_cache; - if (td == NULL || td->hcsplt == 0 || + if (td == NULL || td->did_nak != 0 || (td->hcchar & HCCHAR_EPTYPE_MASK) != (UE_BULK << HCCHAR_EPTYPE_SHIFT)) { continue; } + needsof = 1; + + if (td->hcsplt == 0) + continue; + if ((temp & 7) == 0) { /* Reset state if stuck waiting for complete split */ if (td->state == DWC_CHAN_ST_WAIT_C_PKT) @@ -2501,7 +2542,6 @@ dwc_otg_update_host_transfer_state(struc td->tt_complete_slot = pinfo->slot_index + 2; if (td->tt_complete_slot < 8) { td->tt_scheduled = 1; - needsof = 1; TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry); TAILQ_INSERT_TAIL(&head, xfer, wait_entry); } else { @@ -2621,9 +2661,7 @@ repeat: got_rx_status = 1; } - /* update SOF related information */ - dwc_otg_update_host_transfer_state(sc); - + /* scan for completion events first */ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (!dwc_otg_xfer_do_fifo(xfer)) { /* queue has been modified */ @@ -2631,13 +2669,26 @@ repeat: } } + if (sc->sc_flags.status_device_mode == 0) { + /* update host transfer schedule, so that new transfers can be issued */ + dwc_otg_update_host_transfer_schedule(sc); + + /* start re-scheduled transfers */ + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + if (!dwc_otg_xfer_do_fifo(xfer)) { + /* queue has been modified */ + goto repeat; + } + } + } + if (got_rx_status) { /* check if data was consumed */ if (sc->sc_last_rx_status == 0) goto repeat; /* disable RX FIFO level interrupt */ - sc->sc_irq_mask &= ~GINTSTS_RXFLVL; + sc->sc_irq_mask &= ~GINTMSK_RXFLVLMSK; DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); } } @@ -2699,6 +2750,12 @@ dwc_otg_interrupt(struct dwc_otg_softc * sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; + /* 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 */ dwc_otg_root_intr(sc); } @@ -2732,10 +2789,12 @@ dwc_otg_interrupt(struct dwc_otg_softc * else sc->sc_flags.status_high_speed = 0; - /* disable resume interrupt and enable suspend interrupt */ - - sc->sc_irq_mask &= ~GINTSTS_WKUPINT; - sc->sc_irq_mask |= GINTSTS_USBSUSP; + /* + * Disable resume and SOF interrupt, and enable + * suspend and RX frame interrupt: + */ + sc->sc_irq_mask &= ~(GINTMSK_WKUPINTMSK | GINTMSK_SOFMSK); + sc->sc_irq_mask |= (GINTMSK_USBSUSPMSK | GINTMSK_RXFLVLMSK); DWC_OTG_WRITE_4(sc, DOTG_GINTMSK, sc->sc_irq_mask); /* complete root HUB interrupt endpoint */ @@ -2805,6 +2864,13 @@ dwc_otg_interrupt(struct dwc_otg_softc * /* 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); } /* @@ -2853,10 +2919,6 @@ dwc_otg_interrupt(struct dwc_otg_softc * } } - /* check for SOF interrupt */ - if (status & GINTSTS_SOF) - dwc_otg_update_host_transfer_state(sc); - /* poll FIFO(s) */ dwc_otg_interrupt_poll(sc); @@ -3223,9 +3285,6 @@ dwc_otg_start_standard_chain(struct usb_ DPRINTFN(9, "\n"); - /* update SOF related information */ - dwc_otg_update_host_transfer_state(sc); - /* poll one time - will turn on interrupts */ if (dwc_otg_xfer_do_fifo(xfer)) { @@ -3237,6 +3296,9 @@ dwc_otg_start_standard_chain(struct usb_ usbd_transfer_timeout_ms(xfer, &dwc_otg_timeout, xfer->timeout); } + + /* enable SOF interrupt, if any */ + dwc_otg_enable_sof_irq(sc); } }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405060912.s469CWnZ097391>