diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c index fd0e25497c43..18ebaa56897f 100644 --- a/sys/dev/usb/controller/xhci.c +++ b/sys/dev/usb/controller/xhci.c @@ -90,6 +90,7 @@ #ifdef USB_DEBUG static int xhcidebug; static int xhciroute; +static int xhcipolling; static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, @@ -98,6 +99,9 @@ TUNABLE_INT("hw.usb.xhci.debug", &xhcidebug); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, xhci_port_route, CTLFLAG_RW | CTLFLAG_TUN, &xhciroute, 0, "Routing bitmap for switching EHCI ports to XHCI controller"); TUNABLE_INT("hw.usb.xhci.xhci_port_route", &xhciroute); +SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RW | CTLFLAG_TUN, + &xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller"); +TUNABLE_INT("hw.usb.xhci.use_polling", &xhcipolling); #endif #define XHCI_INTR_ENDPT 1 @@ -194,6 +198,16 @@ xhci_get_port_route(void) #endif } +uint8_t +xhci_use_polling(void) +{ +#ifdef USB_DEBUG + return (xhcipolling != 0); +#else + return (0); +#endif +} + static void xhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) { diff --git a/sys/dev/usb/controller/xhci.h b/sys/dev/usb/controller/xhci.h index d02c26f7d040..0872f402cbc2 100644 --- a/sys/dev/usb/controller/xhci.h +++ b/sys/dev/usb/controller/xhci.h @@ -438,6 +438,8 @@ struct xhci_softc { /* configure message */ struct usb_bus_msg sc_config_msg[2]; + struct usb_callout sc_callout; + union xhci_hub_desc sc_hub_desc; struct cv sc_cmd_cv; @@ -500,6 +502,7 @@ struct xhci_softc { /* prototypes */ uint32_t xhci_get_port_route(void); +uint8_t xhci_use_polling(void); usb_error_t xhci_halt_controller(struct xhci_softc *); usb_error_t xhci_init(struct xhci_softc *, device_t); usb_error_t xhci_start_controller(struct xhci_softc *); diff --git a/sys/dev/usb/controller/xhci_pci.c b/sys/dev/usb/controller/xhci_pci.c index d2c903591fbc..8695dac13703 100644 --- a/sys/dev/usb/controller/xhci_pci.c +++ b/sys/dev/usb/controller/xhci_pci.c @@ -132,6 +132,16 @@ xhci_pci_probe(device_t self) } } +static void +xhci_interrupt_poll(void *_sc) +{ + struct xhci_softc *sc = _sc; + USB_BUS_UNLOCK(&sc->sc_bus); + xhci_interrupt(sc); + USB_BUS_LOCK(&sc->sc_bus); + usb_callout_reset(&sc->sc_callout, 1, (void *)&xhci_interrupt_poll, sc); +} + static int xhci_pci_attach(device_t self) { @@ -159,12 +169,13 @@ xhci_pci_attach(device_t self) sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); + usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0); + rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(self, "Could not allocate IRQ\n"); - goto error; } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (sc->sc_bus.bdev == NULL) { @@ -175,18 +186,22 @@ xhci_pci_attach(device_t self) sprintf(sc->sc_vendor, "0x%04x", pci_get_vendor(self)); -#if (__FreeBSD_version >= 700031) - err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl); -#else - err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl); -#endif - if (err) { - device_printf(self, "Could not setup IRQ, err=%d\n", err); - sc->sc_intr_hdl = NULL; - goto error; + if (sc->sc_irq_res != NULL) { + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl); + if (err != 0) { + device_printf(self, "Could not setup IRQ, err=%d\n", err); + sc->sc_intr_hdl = NULL; + } } + if (sc->sc_irq_res == NULL || sc->sc_intr_hdl == NULL || + xhci_use_polling() != 0) { + device_printf(self, "Interrupt polling at %dHz\n", hz); + USB_BUS_LOCK(&sc->sc_bus); + xhci_interrupt_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); + } + xhci_pci_take_controller(self); err = xhci_halt_controller(sc); @@ -222,12 +237,14 @@ xhci_pci_detach(device_t self) /* during module unload there are lots of children leftover */ device_delete_children(self); + if (sc->sc_io_res) { + usb_callout_drain(&sc->sc_callout); + xhci_halt_controller(sc); + } + pci_disable_busmaster(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { - - xhci_halt_controller(sc); - bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); sc->sc_intr_hdl = NULL; }