Index: head/sys/arm/allwinner/a10_ehci.c =================================================================== --- head/sys/arm/allwinner/a10_ehci.c (revision 276716) +++ head/sys/arm/allwinner/a10_ehci.c (revision 276717) @@ -1,304 +1,305 @@ /*- * Copyright (c) 2012 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Allwinner A10 attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "a10_clk.h" #define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" #define SW_USB_PMU_IRQ_ENABLE 0x800 #define SW_SDRAM_REG_HPCR_USB1 (0x250 + ((1 << 2) * 4)) #define SW_SDRAM_REG_HPCR_USB2 (0x250 + ((1 << 2) * 5)) #define SW_SDRAM_BP_HPCR_ACCESS (1 << 0) #define SW_ULPI_BYPASS (1 << 0) #define SW_AHB_INCRX_ALIGN (1 << 8) #define SW_AHB_INCR4 (1 << 9) #define SW_AHB_INCR8 (1 << 10) #define GPIO_USB1_PWR 230 #define GPIO_USB2_PWR 227 #define A10_READ_4(sc, reg) \ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) #define A10_WRITE_4(sc, reg, data) \ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) static device_attach_t a10_ehci_attach; static device_detach_t a10_ehci_detach; bs_r_1_proto(reversed); bs_w_1_proto(reversed); static int a10_ehci_probe(device_t self) { if (!ofw_bus_status_okay(self)) return (ENXIO); if (!ofw_bus_is_compatible(self, "allwinner,usb-ehci")) return (ENXIO); device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } static int a10_ehci_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); bus_space_handle_t bsh; device_t sc_gpio_dev; int err; int rid; uint32_t reg_value = 0; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_bus.usbrev = USB_REV_2_0; rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); bsh = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); if (bus_space_subregion(sc->sc_io_tag, bsh, 0x00, sc->sc_io_size, &sc->sc_io_hdl) != 0) panic("%s: unable to subregion USB host registers", device_get_name(self)); 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Allwinner"); /* Get the GPIO device, we need this to give power to USB */ sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (sc_gpio_dev == NULL) { device_printf(self, "Error: failed to get the GPIO device\n"); goto error; } err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } sc->sc_flags |= EHCI_SCFLG_DONTRESET; /* Enable clock for USB */ a10_clk_usb_activate(); /* Give power to USB */ GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_OUTPUT); GPIO_PIN_SET(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_HIGH); /* Give power to USB */ GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_OUTPUT); GPIO_PIN_SET(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_HIGH); /* Enable passby */ reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); /* Configure port */ reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); reg_value |= SW_SDRAM_BP_HPCR_ACCESS; A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: a10_ehci_detach(self); return (ENXIO); } static int a10_ehci_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); device_t bdev; int err; uint32_t reg_value = 0; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); /* Disable configure port */ reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); /* Disable passby */ reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); /* Disable clock for USB */ a10_clk_usb_deactivate(); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10_ehci_probe), DEVMETHOD(device_attach, a10_ehci_attach), DEVMETHOD(device_detach, a10_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, .size = sizeof(ehci_softc_t), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/arm/cavium/cns11xx/ehci_ebus.c =================================================================== --- head/sys/arm/cavium/cns11xx/ehci_ebus.c (revision 276716) +++ head/sys/arm/cavium/cns11xx/ehci_ebus.c (revision 276717) @@ -1,253 +1,254 @@ /*- * Copyright (C) 2009 Yohanes Nugroho * based on ehci_mbus.c * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. * All rights reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of MARVELL nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_attach_t ehci_ebus_attach; static device_detach_t ehci_ebus_detach; static void *ih_err; #define EHCI_HC_DEVSTR "CNS11XX USB EHCI" #define USB_BRIDGE_INTR_MASK 0x214 static int ehci_ebus_probe(device_t self) { device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } static int ehci_ebus_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); bus_space_handle_t bsh; int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_bus.usbrev = USB_REV_2_0; rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); bsh = rman_get_bushandle(sc->sc_io_res); /*magic, undocumented initialization*/ bus_space_write_4((sc)->sc_io_tag, bsh, 0x04, 0x106); bus_space_write_4((sc)->sc_io_tag, bsh, 0x40, (3 << 5)|0x2000); DELAY(1000); sc->sc_io_size = 4096; if (bus_space_subregion(sc->sc_io_tag, bsh, 0x4000000, sc->sc_io_size, &sc->sc_io_hdl) != 0) panic("%s: unable to subregion USB host registers", device_get_name(self)); 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"); ehci_ebus_detach(self); return (ENXIO); } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Cavium"); err = bus_setup_intr(self,sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup error irq, %d\n", err); ih_err = NULL; goto error; } err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: ehci_ebus_detach(self); return (ENXIO); } static int ehci_ebus_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); /* * disable interrupts that might have been switched on in * ehci_ebus_attach() */ if (sc->sc_io_res) { EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); } if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ehci_ebus_probe), DEVMETHOD(device_attach, ehci_ebus_attach), DEVMETHOD(device_detach, ehci_ebus_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, .size = sizeof(ehci_softc_t), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, econaarm, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/arm/cavium/cns11xx/ohci_ec.c =================================================================== --- head/sys/arm/cavium/cns11xx/ohci_ec.c (revision 276716) +++ head/sys/arm/cavium/cns11xx/ohci_ec.c (revision 276717) @@ -1,239 +1,240 @@ /*- * Copyright (c) 2009 Yohanes Nugroho * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 static device_probe_t ohci_ec_probe; static device_attach_t ohci_ec_attach; static device_detach_t ohci_ec_detach; struct ec_ohci_softc { struct ohci_softc sc_ohci; /* must be first */ }; static int ohci_ec_probe(device_t dev) { device_set_desc(dev, "Econa integrated OHCI controller"); return (BUS_PROBE_DEFAULT); } static int ohci_ec_attach(device_t dev) { struct ec_ohci_softc *sc = device_get_softc(dev); bus_space_handle_t bsh; int err; int rid; /* initialise some bus fields */ sc->sc_ohci.sc_bus.parent = dev; sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_ohci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_ohci.sc_dev = dev; rid = MEM_RID; sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); bsh = rman_get_bushandle(sc->sc_ohci.sc_io_res); /* Undocumented magic initialization */ bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x04, 0x146); bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x44, 0x0200); DELAY(1000); sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); if (bus_space_subregion(sc->sc_ohci.sc_io_tag, bsh, 0x4000000, sc->sc_ohci.sc_io_size, &sc->sc_ohci.sc_io_hdl) != 0) panic("%s: unable to subregion USB host registers", device_get_name(dev)); rid = 0; sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_irq_res)) { goto error; } sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_ohci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); strlcpy(sc->sc_ohci.sc_vendor, "Cavium", sizeof(sc->sc_ohci.sc_vendor)); #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); #endif if (err) { sc->sc_ohci.sc_intr_hdl = NULL; goto error; } bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(&sc->sc_ohci); if (!err) { err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); } if (err) { goto error; } return (0); error: ohci_ec_detach(dev); return (ENXIO); } static int ohci_ec_detach(device_t dev) { struct ec_ohci_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_ohci.sc_bus.bdev) { bdev = sc->sc_ohci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(&sc->sc_ohci); err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); sc->sc_ohci.sc_intr_hdl = NULL; } if (sc->sc_ohci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); sc->sc_ohci.sc_irq_res = NULL; } if (sc->sc_ohci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_ec_probe), DEVMETHOD(device_attach, ohci_ec_attach), DEVMETHOD(device_detach, ohci_ec_detach), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_driver = { .name = "ohci", .methods = ohci_methods, .size = sizeof(struct ec_ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, econaarm, ohci_driver, ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb, 1, 1, 1); Index: head/sys/arm/freescale/vybrid/vf_ehci.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_ehci.c (revision 276716) +++ head/sys/arm/freescale/vybrid/vf_ehci.c (revision 276717) @@ -1,419 +1,420 @@ /*- * Copyright (c) 2013 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Vybrid Family Universal Serial Bus (USB) Controller * Chapter 44-45, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "opt_platform.h" #define ENUTMILEVEL3 (1 << 15) #define ENUTMILEVEL2 (1 << 14) #define GPIO_USB_PWR 134 #define USB_ID 0x000 /* Identification register */ #define USB_HWGENERAL 0x004 /* Hardware General */ #define USB_HWHOST 0x008 /* Host Hardware Parameters */ #define USB_HWDEVICE 0x00C /* Device Hardware Parameters */ #define USB_HWTXBUF 0x010 /* TX Buffer Hardware Parameters */ #define USB_HWRXBUF 0x014 /* RX Buffer Hardware Parameters */ #define USB_HCSPARAMS 0x104 /* Host Controller Structural Parameters */ #define USBPHY_PWD 0x00 /* PHY Power-Down Register */ #define USBPHY_PWD_SET 0x04 /* PHY Power-Down Register */ #define USBPHY_PWD_CLR 0x08 /* PHY Power-Down Register */ #define USBPHY_PWD_TOG 0x0C /* PHY Power-Down Register */ #define USBPHY_TX 0x10 /* PHY Transmitter Control Register */ #define USBPHY_RX 0x20 /* PHY Receiver Control Register */ #define USBPHY_RX_SET 0x24 /* PHY Receiver Control Register */ #define USBPHY_RX_CLR 0x28 /* PHY Receiver Control Register */ #define USBPHY_RX_TOG 0x2C /* PHY Receiver Control Register */ #define USBPHY_CTRL 0x30 /* PHY General Control Register */ #define USBPHY_CTRL_SET 0x34 /* PHY General Control Register */ #define USBPHY_CTRL_CLR 0x38 /* PHY General Control Register */ #define USBPHY_CTRL_TOG 0x3C /* PHY General Control Register */ #define USBPHY_STATUS 0x40 /* PHY Status Register */ #define USBPHY_DEBUG 0x50 /* PHY Debug Register */ #define USBPHY_DEBUG_SET 0x54 /* PHY Debug Register */ #define USBPHY_DEBUG_CLR 0x58 /* PHY Debug Register */ #define USBPHY_DEBUG_TOG 0x5C /* PHY Debug Register */ #define USBPHY_DEBUG0_STATUS 0x60 /* UTMI Debug Status Register 0 */ #define USBPHY_DEBUG1 0x70 /* UTMI Debug Status Register 1 */ #define USBPHY_DEBUG1_SET 0x74 /* UTMI Debug Status Register 1 */ #define USBPHY_DEBUG1_CLR 0x78 /* UTMI Debug Status Register 1 */ #define USBPHY_DEBUG1_TOG 0x7C /* UTMI Debug Status Register 1 */ #define USBPHY_VERSION 0x80 /* UTMI RTL Version */ #define USBPHY_IP 0x90 /* PHY IP Block Register */ #define USBPHY_IP_SET 0x94 /* PHY IP Block Register */ #define USBPHY_IP_CLR 0x98 /* PHY IP Block Register */ #define USBPHY_IP_TOG 0x9C /* PHY IP Block Register */ #define USBPHY_CTRL_SFTRST (1U << 31) #define USBPHY_CTRL_CLKGATE (1 << 30) #define USBPHY_DEBUG_CLKGATE (1 << 30) #define PHY_READ4(_sc, _reg) \ bus_space_read_4(_sc->bst_phy, _sc->bsh_phy, _reg) #define PHY_WRITE4(_sc, _reg, _val) \ bus_space_write_4(_sc->bst_phy, _sc->bsh_phy, _reg, _val) #define USBC_READ4(_sc, _reg) \ bus_space_read_4(_sc->bst_usbc, _sc->bsh_usbc, _reg) #define USBC_WRITE4(_sc, _reg, _val) \ bus_space_write_4(_sc->bst_usbc, _sc->bsh_usbc, _reg, _val) /* Forward declarations */ static int vybrid_ehci_attach(device_t dev); static int vybrid_ehci_detach(device_t dev); static int vybrid_ehci_probe(device_t dev); struct vybrid_ehci_softc { ehci_softc_t base; device_t dev; struct resource *res[6]; bus_space_tag_t bst_phy; bus_space_handle_t bsh_phy; bus_space_tag_t bst_usbc; bus_space_handle_t bsh_usbc; }; static struct resource_spec vybrid_ehci_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, vybrid_ehci_probe), DEVMETHOD(device_attach, vybrid_ehci_attach), DEVMETHOD(device_detach, vybrid_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; /* kobj_class definition */ static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(ehci_softc_t) }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); /* * Public methods */ static int vybrid_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "fsl,mvf600-usb-ehci") == 0) return (ENXIO); device_set_desc(dev, "Vybrid Family integrated USB controller"); return (BUS_PROBE_DEFAULT); } static int phy_init(struct vybrid_ehci_softc *esc) { device_t sc_gpio_dev; int reg; /* Reset phy */ reg = PHY_READ4(esc, USBPHY_CTRL); reg |= (USBPHY_CTRL_SFTRST); PHY_WRITE4(esc, USBPHY_CTRL, reg); /* Minimum reset time */ DELAY(10000); reg &= ~(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE); PHY_WRITE4(esc, USBPHY_CTRL, reg); reg = (ENUTMILEVEL2 | ENUTMILEVEL3); PHY_WRITE4(esc, USBPHY_CTRL_SET, reg); /* Get the GPIO device, we need this to give power to USB */ sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (sc_gpio_dev == NULL) { device_printf(esc->dev, "Error: failed to get the GPIO dev\n"); return (1); } /* Give power to USB */ GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB_PWR, GPIO_PIN_OUTPUT); GPIO_PIN_SET(sc_gpio_dev, GPIO_USB_PWR, GPIO_PIN_HIGH); /* Power up PHY */ PHY_WRITE4(esc, USBPHY_PWD, 0x00); /* Ungate clocks */ reg = PHY_READ4(esc, USBPHY_DEBUG); reg &= ~(USBPHY_DEBUG_CLKGATE); PHY_WRITE4(esc, USBPHY_DEBUG, reg); #if 0 printf("USBPHY_CTRL == 0x%08x\n", PHY_READ4(esc, USBPHY_CTRL)); printf("USBPHY_IP == 0x%08x\n", PHY_READ4(esc, USBPHY_IP)); printf("USBPHY_STATUS == 0x%08x\n", PHY_READ4(esc, USBPHY_STATUS)); printf("USBPHY_DEBUG == 0x%08x\n", PHY_READ4(esc, USBPHY_DEBUG)); printf("USBPHY_DEBUG0_STATUS == 0x%08x\n", PHY_READ4(esc, USBPHY_DEBUG0_STATUS)); printf("USBPHY_DEBUG1 == 0x%08x\n", PHY_READ4(esc, USBPHY_DEBUG1)); #endif return (0); } static int vybrid_ehci_attach(device_t dev) { struct vybrid_ehci_softc *esc; ehci_softc_t *sc; bus_space_handle_t bsh; int err; int reg; esc = device_get_softc(dev); esc->dev = dev; sc = &esc->base; sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; if (bus_alloc_resources(dev, vybrid_ehci_spec, esc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* EHCI registers */ sc->sc_io_tag = rman_get_bustag(esc->res[0]); bsh = rman_get_bushandle(esc->res[0]); sc->sc_io_size = rman_get_size(esc->res[0]); esc->bst_usbc = rman_get_bustag(esc->res[1]); esc->bsh_usbc = rman_get_bushandle(esc->res[1]); esc->bst_phy = rman_get_bustag(esc->res[2]); esc->bsh_phy = rman_get_bushandle(esc->res[2]); /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) return (ENXIO); #if 0 printf("USBx_HCSPARAMS is 0x%08x\n", bus_space_read_4(sc->sc_io_tag, bsh, USB_HCSPARAMS)); printf("USB_ID == 0x%08x\n", bus_space_read_4(sc->sc_io_tag, bsh, USB_ID)); printf("USB_HWGENERAL == 0x%08x\n", bus_space_read_4(sc->sc_io_tag, bsh, USB_HWGENERAL)); printf("USB_HWHOST == 0x%08x\n", bus_space_read_4(sc->sc_io_tag, bsh, USB_HWHOST)); printf("USB_HWDEVICE == 0x%08x\n", bus_space_read_4(sc->sc_io_tag, bsh, USB_HWDEVICE)); printf("USB_HWTXBUF == 0x%08x\n", bus_space_read_4(sc->sc_io_tag, bsh, USB_HWTXBUF)); printf("USB_HWRXBUF == 0x%08x\n", bus_space_read_4(sc->sc_io_tag, bsh, USB_HWRXBUF)); #endif if (phy_init(esc)) { device_printf(dev, "Could not setup PHY\n"); return (1); } /* * Set handle to USB related registers subregion used by * generic EHCI driver. */ err = bus_space_subregion(sc->sc_io_tag, bsh, 0x100, sc->sc_io_size, &sc->sc_io_hdl); if (err != 0) return (ENXIO); /* Setup interrupt handler */ err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not setup irq, " "%d\n", err); return (1); } /* Add USB device */ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); err = bus_teardown_intr(dev, esc->res[5], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," " %d\n", err); return (1); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); strlcpy(sc->sc_vendor, "Freescale", sizeof(sc->sc_vendor)); /* Set host mode */ reg = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, 0xA8); reg |= 0x3; bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 0xA8, reg); /* Set flags */ sc->sc_flags |= EHCI_SCFLG_SETMODE | EHCI_SCFLG_NORESTERM; err = ehci_init(sc); if (!err) { sc->sc_flags |= EHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); } else { device_printf(dev, "USB init failed err=%d\n", err); device_delete_child(dev, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; err = bus_teardown_intr(dev, esc->res[5], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," " %d\n", err); return (1); } return (0); } static int vybrid_ehci_detach(device_t dev) { struct vybrid_ehci_softc *esc; ehci_softc_t *sc; int err; esc = device_get_softc(dev); sc = &esc->base; if (sc->sc_flags & EHCI_SCFLG_DONEINIT) return (0); /* * only call ehci_detach() after ehci_init() */ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { ehci_detach(sc); sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; } /* * Disable interrupts that might have been switched on in * ehci_init. */ if (sc->sc_io_tag && sc->sc_io_hdl) bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, EHCI_USBINTR, 0); if (esc->res[5] && sc->sc_intr_hdl) { err = bus_teardown_intr(dev, esc->res[5], sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not tear down irq," " %d\n", err); return (err); } sc->sc_intr_hdl = NULL; } if (sc->sc_bus.bdev) { device_delete_child(dev, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; } /* During module unload there are lots of children leftover */ device_delete_children(dev); bus_release_resources(dev, vybrid_ehci_spec, esc->res); return (0); } Index: head/sys/arm/lpc/lpc_ohci.c =================================================================== --- head/sys/arm/lpc/lpc_ohci.c (revision 276716) +++ head/sys/arm/lpc/lpc_ohci.c (revision 276717) @@ -1,358 +1,359 @@ /*- * Copyright (c) 2011 Jakub Wojciech Klama * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define I2C_START_BIT (1 << 8) #define I2C_STOP_BIT (1 << 9) #define I2C_READ 0x01 #define I2C_WRITE 0x00 #define DUMMY_BYTE 0x55 #define lpc_otg_read_4(_sc, _reg) \ bus_space_read_4(_sc->sc_io_tag, _sc->sc_io_hdl, _reg) #define lpc_otg_write_4(_sc, _reg, _value) \ bus_space_write_4(_sc->sc_io_tag, _sc->sc_io_hdl, _reg, _value) #define lpc_otg_wait_write_4(_sc, _wreg, _sreg, _value) \ do { \ lpc_otg_write_4(_sc, _wreg, _value); \ while ((lpc_otg_read_4(_sc, _sreg) & _value) != _value); \ } while (0); static int lpc_ohci_probe(device_t dev); static int lpc_ohci_attach(device_t dev); static int lpc_ohci_detach(device_t dev); static void lpc_otg_i2c_reset(struct ohci_softc *); static int lpc_isp3101_read(struct ohci_softc *, int); static void lpc_isp3101_write(struct ohci_softc *, int, int); static void lpc_isp3101_clear(struct ohci_softc *, int, int); static void lpc_isp3101_configure(device_t dev, struct ohci_softc *); static int lpc_ohci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "lpc,usb-ohci")) return (ENXIO); device_set_desc(dev, "LPC32x0 USB OHCI controller"); return (BUS_PROBE_DEFAULT); } static int lpc_ohci_attach(device_t dev) { struct ohci_softc *sc = device_get_softc(dev); int err; int rid; int i = 0; uint32_t usbctrl; uint32_t otgstatus; sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) return (ENOMEM); rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(dev, "cannot map OHCI register space\n"); goto fail; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "cannot allocate interrupt\n"); goto fail; } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_bus.bdev)) goto fail; device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); strlcpy(sc->sc_vendor, "NXP", sizeof(sc->sc_vendor)); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)ohci_interrupt, sc, &sc->sc_intr_hdl); if (err) { sc->sc_intr_hdl = NULL; goto fail; } usbctrl = lpc_pwr_read(dev, LPC_CLKPWR_USB_CTRL); usbctrl |= LPC_CLKPWR_USB_CTRL_SLAVE_HCLK | LPC_CLKPWR_USB_CTRL_BUSKEEPER; lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); /* Enable OTG I2C clock */ lpc_otg_wait_write_4(sc, LPC_OTG_CLOCK_CTRL, LPC_OTG_CLOCK_STATUS, LPC_OTG_CLOCK_CTRL_I2C_EN); /* Reset OTG I2C bus */ lpc_otg_i2c_reset(sc); lpc_isp3101_configure(dev, sc); /* Configure PLL */ usbctrl &= ~(LPC_CLKPWR_USB_CTRL_CLK_EN1 | LPC_CLKPWR_USB_CTRL_CLK_EN2); lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); usbctrl |= LPC_CLKPWR_USB_CTRL_CLK_EN1; lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); usbctrl |= LPC_CLKPWR_USB_CTRL_FDBKDIV(192-1); usbctrl |= LPC_CLKPWR_USB_CTRL_POSTDIV(1); usbctrl |= LPC_CLKPWR_USB_CTRL_PLL_PDOWN; lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); do { usbctrl = lpc_pwr_read(dev, LPC_CLKPWR_USB_CTRL); if (i++ > 100000) { device_printf(dev, "USB OTG PLL doesn't lock!\n"); goto fail; } } while ((usbctrl & LPC_CLKPWR_USB_CTRL_PLL_LOCK) == 0); usbctrl |= LPC_CLKPWR_USB_CTRL_CLK_EN2; usbctrl |= LPC_CLKPWR_USB_CTRL_HOST_NEED_CLK_EN; lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); lpc_otg_wait_write_4(sc, LPC_OTG_CLOCK_CTRL, LPC_OTG_CLOCK_STATUS, (LPC_OTG_CLOCK_CTRL_AHB_EN | LPC_OTG_CLOCK_CTRL_OTG_EN | LPC_OTG_CLOCK_CTRL_I2C_EN | LPC_OTG_CLOCK_CTRL_HOST_EN)); otgstatus = lpc_otg_read_4(sc, LPC_OTG_STATUS); lpc_otg_write_4(sc, LPC_OTG_STATUS, otgstatus | LPC_OTG_STATUS_HOST_EN); lpc_isp3101_write(sc, LPC_ISP3101_OTG_CONTROL_1, LPC_ISP3101_OTG1_VBUS_DRV); err = ohci_init(sc); if (err) goto fail; err = device_probe_and_attach(sc->sc_bus.bdev); if (err) goto fail; return (0); fail: if (sc->sc_intr_hdl) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_io_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); return (ENXIO); } static int lpc_isp3101_read(struct ohci_softc *sc, int reg) { int status; int i = 0; lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, (LPC_ISP3101_I2C_ADDR << 1) | I2C_START_BIT); lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, reg); lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, (LPC_ISP3101_I2C_ADDR << 1) | I2C_START_BIT | I2C_READ); lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, I2C_STOP_BIT | DUMMY_BYTE); do { status = lpc_otg_read_4(sc, LPC_OTG_I2C_STATUS); i++; } while ((status & LPC_OTG_I2C_STATUS_TDI) == 0 || i < 100000); lpc_otg_write_4(sc, LPC_OTG_I2C_STATUS, LPC_OTG_I2C_STATUS_TDI); return (lpc_otg_read_4(sc, LPC_OTG_I2C_TXRX) & 0xff); } static void lpc_otg_i2c_reset(struct ohci_softc *sc) { int ctrl; int i = 0; lpc_otg_write_4(sc, LPC_OTG_I2C_CLKHI, 0x3f); lpc_otg_write_4(sc, LPC_OTG_I2C_CLKLO, 0x3f); ctrl = lpc_otg_read_4(sc, LPC_OTG_I2C_CTRL); lpc_otg_write_4(sc, LPC_OTG_I2C_CTRL, ctrl | LPC_OTG_I2C_CTRL_SRST); do { ctrl = lpc_otg_read_4(sc, LPC_OTG_I2C_CTRL); i++; } while (ctrl & LPC_OTG_I2C_CTRL_SRST); } static void lpc_isp3101_write(struct ohci_softc *sc, int reg, int value) { int status; int i = 0; bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_TXRX, (LPC_ISP3101_I2C_ADDR << 1) | I2C_START_BIT); bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_TXRX, (reg | I2C_WRITE)); bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_TXRX, (value | I2C_STOP_BIT)); do { status = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_STATUS); i++; } while ((status & LPC_OTG_I2C_STATUS_TDI) == 0 || i < 100000); bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_STATUS, LPC_OTG_I2C_STATUS_TDI); } static __inline void lpc_isp3101_clear(struct ohci_softc *sc, int reg, int value) { lpc_isp3101_write(sc, (reg | LPC_ISP3101_REG_CLEAR_ADDR), value); } static void lpc_isp3101_configure(device_t dev, struct ohci_softc *sc) { lpc_isp3101_clear(sc, LPC_ISP3101_MODE_CONTROL_1, LPC_ISP3101_MC1_UART_EN); lpc_isp3101_clear(sc, LPC_ISP3101_MODE_CONTROL_1, ~LPC_ISP3101_MC1_SPEED_REG); lpc_isp3101_write(sc, LPC_ISP3101_MODE_CONTROL_1, LPC_ISP3101_MC1_SPEED_REG); lpc_isp3101_clear(sc, LPC_ISP3101_MODE_CONTROL_2, ~0); lpc_isp3101_write(sc, LPC_ISP3101_MODE_CONTROL_2, (LPC_ISP3101_MC2_BI_DI | LPC_ISP3101_MC2_PSW_EN | LPC_ISP3101_MC2_SPD_SUSP_CTRL)); lpc_isp3101_clear(sc, LPC_ISP3101_OTG_CONTROL_1, ~0); lpc_isp3101_write(sc, LPC_ISP3101_MODE_CONTROL_1, LPC_ISP3101_MC1_DAT_SE0); lpc_isp3101_write(sc, LPC_ISP3101_OTG_CONTROL_1, (LPC_ISP3101_OTG1_DM_PULLDOWN | LPC_ISP3101_OTG1_DP_PULLDOWN)); lpc_isp3101_clear(sc, LPC_ISP3101_OTG_CONTROL_1, (LPC_ISP3101_OTG1_DM_PULLUP | LPC_ISP3101_OTG1_DP_PULLUP)); lpc_isp3101_clear(sc, LPC_ISP3101_OTG_INTR_LATCH, ~0); lpc_isp3101_clear(sc, LPC_ISP3101_OTG_INTR_FALLING, ~0); lpc_isp3101_clear(sc, LPC_ISP3101_OTG_INTR_RISING, ~0); device_printf(dev, "ISP3101 PHY \n", (lpc_isp3101_read(sc, 0x00) | (lpc_isp3101_read(sc, 0x01) << 8)), (lpc_isp3101_read(sc, 0x03) | (lpc_isp3101_read(sc, 0x04) << 8)), (lpc_isp3101_read(sc, 0x14) | (lpc_isp3101_read(sc, 0x15) << 8))); } static int lpc_ohci_detach(device_t dev) { return (0); } static device_method_t lpc_ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lpc_ohci_probe), DEVMETHOD(device_attach, lpc_ohci_attach), DEVMETHOD(device_detach, lpc_ohci_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t lpc_ohci_driver = { "ohci", lpc_ohci_methods, sizeof(struct ohci_softc), }; static devclass_t lpc_ohci_devclass; DRIVER_MODULE(ohci, simplebus, lpc_ohci_driver, lpc_ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb, 1, 1, 1); Index: head/sys/arm/samsung/exynos/exynos5_ehci.c =================================================================== --- head/sys/arm/samsung/exynos/exynos5_ehci.c (revision 276716) +++ head/sys/arm/samsung/exynos/exynos5_ehci.c (revision 276717) @@ -1,393 +1,394 @@ /*- * Copyright (c) 2013-2014 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "opt_platform.h" /* GPIO control */ #define GPIO_OUTPUT 1 #define GPIO_INPUT 0 #define PIN_USB 161 /* SYSREG */ #define EXYNOS5_SYSREG_USB2_PHY 0x0 #define USB2_MODE_HOST 0x1 /* USB HOST */ #define HOST_CTRL_CLK_24MHZ (5 << 16) #define HOST_CTRL_CLK_MASK (7 << 16) #define HOST_CTRL_SIDDQ (1 << 6) #define HOST_CTRL_SLEEP (1 << 5) #define HOST_CTRL_SUSPEND (1 << 4) #define HOST_CTRL_RESET_LINK (1 << 1) #define HOST_CTRL_RESET_PHY (1 << 0) #define HOST_CTRL_RESET_PHY_ALL (1U << 31) /* Forward declarations */ static int exynos_ehci_attach(device_t dev); static int exynos_ehci_detach(device_t dev); static int exynos_ehci_probe(device_t dev); struct exynos_ehci_softc { device_t dev; ehci_softc_t base; struct resource *res[4]; bus_space_tag_t host_bst; bus_space_tag_t sysreg_bst; bus_space_handle_t host_bsh; bus_space_handle_t sysreg_bsh; }; static struct resource_spec exynos_ehci_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, exynos_ehci_probe), DEVMETHOD(device_attach, exynos_ehci_attach), DEVMETHOD(device_detach, exynos_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; /* kobj_class definition */ static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(ehci_softc_t) }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); /* * Public methods */ static int exynos_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "exynos,usb-ehci") == 0) return (ENXIO); device_set_desc(dev, "Exynos integrated USB controller"); return (BUS_PROBE_DEFAULT); } static int gpio_ctrl(struct exynos_ehci_softc *esc, int dir, int power) { device_t gpio_dev; /* Get the GPIO device, we need this to give power to USB */ gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (gpio_dev == NULL) { device_printf(esc->dev, "cant find gpio_dev\n"); return (1); } if (power) GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_HIGH); else GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_LOW); if (dir) GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_OUTPUT); else GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_INPUT); return (0); } static int reset_hsic_hub(struct exynos_ehci_softc *esc, phandle_t hub) { device_t gpio_dev; pcell_t pin; /* TODO: check that hub is compatible with "smsc,usb3503" */ if (!OF_hasprop(hub, "freebsd,reset-gpio")) { return (1); } if (OF_getencprop(hub, "freebsd,reset-gpio", &pin, sizeof(pin)) < 0) { device_printf(esc->dev, "failed to decode reset GPIO pin number for HSIC hub\n"); return (1); } /* Get the GPIO device, we need this to give power to USB */ gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (gpio_dev == NULL) { device_printf(esc->dev, "Cant find gpio device\n"); return (1); } GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_LOW); DELAY(100); GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_HIGH); return (0); } static int phy_init(struct exynos_ehci_softc *esc) { int reg; phandle_t hub; gpio_ctrl(esc, GPIO_INPUT, 1); /* set USB HOST mode */ bus_space_write_4(esc->sysreg_bst, esc->sysreg_bsh, EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST); /* Power ON phy */ usb2_phy_power_on(); reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); reg &= ~(HOST_CTRL_CLK_MASK | HOST_CTRL_RESET_PHY | HOST_CTRL_RESET_PHY_ALL | HOST_CTRL_SIDDQ | HOST_CTRL_SUSPEND | HOST_CTRL_SLEEP); reg |= (HOST_CTRL_CLK_24MHZ | HOST_CTRL_RESET_LINK); bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); DELAY(10); reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); reg &= ~(HOST_CTRL_RESET_LINK); bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); if ((hub = OF_finddevice("/hsichub")) != 0) { reset_hsic_hub(esc, hub); } gpio_ctrl(esc, GPIO_OUTPUT, 1); return (0); } static int exynos_ehci_attach(device_t dev) { struct exynos_ehci_softc *esc; ehci_softc_t *sc; bus_space_handle_t bsh; int err; esc = device_get_softc(dev); esc->dev = dev; sc = &esc->base; sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; if (bus_alloc_resources(dev, exynos_ehci_spec, esc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* EHCI registers */ sc->sc_io_tag = rman_get_bustag(esc->res[0]); bsh = rman_get_bushandle(esc->res[0]); sc->sc_io_size = rman_get_size(esc->res[0]); /* EHCI HOST ctrl registers */ esc->host_bst = rman_get_bustag(esc->res[1]); esc->host_bsh = rman_get_bushandle(esc->res[1]); /* SYSREG */ esc->sysreg_bst = rman_get_bustag(esc->res[2]); esc->sysreg_bsh = rman_get_bushandle(esc->res[2]); /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) return (ENXIO); /* * Set handle to USB related registers subregion used by * generic EHCI driver. */ err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0, sc->sc_io_size, &sc->sc_io_hdl); if (err != 0) return (ENXIO); phy_init(esc); /* Setup interrupt handler */ err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not setup irq, " "%d\n", err); return (1); } /* Add USB device */ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," " %d\n", err); return (1); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); err = ehci_init(sc); if (!err) { sc->sc_flags |= EHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); } else { device_printf(dev, "USB init failed err=%d\n", err); device_delete_child(dev, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," " %d\n", err); return (1); } return (0); } static int exynos_ehci_detach(device_t dev) { struct exynos_ehci_softc *esc; ehci_softc_t *sc; int err; esc = device_get_softc(dev); sc = &esc->base; if (sc->sc_flags & EHCI_SCFLG_DONEINIT) return (0); /* * only call ehci_detach() after ehci_init() */ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { ehci_detach(sc); sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; } /* * Disable interrupts that might have been switched on in * ehci_init. */ if (sc->sc_io_tag && sc->sc_io_hdl) bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, EHCI_USBINTR, 0); if (esc->res[3] && sc->sc_intr_hdl) { err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not tear down irq," " %d\n", err); return (err); } sc->sc_intr_hdl = NULL; } if (sc->sc_bus.bdev) { device_delete_child(dev, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; } /* During module unload there are lots of children leftover */ device_delete_children(dev); bus_release_resources(dev, exynos_ehci_spec, esc->res); return (0); } Index: head/sys/arm/ti/am335x/am335x_usbss.c =================================================================== --- head/sys/arm/ti/am335x/am335x_usbss.c (revision 276716) +++ head/sys/arm/ti/am335x/am335x_usbss.c (revision 276717) @@ -1,494 +1,495 @@ /*- * Copyright (c) 2013 Oleksandr Tymoshenko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usbssdebug #include #include #include #include #include #include #include #include #define AM335X_USB_PORTS 2 #define USBSS_REVREG 0x00 #define USBSS_SYSCONFIG 0x10 #define USBSS_SYSCONFIG_SRESET 1 #define USBCTRL_REV 0x00 #define USBCTRL_CTRL 0x14 #define USBCTRL_STAT 0x18 #define USBCTRL_IRQ_STAT0 0x30 #define IRQ_STAT0_RXSHIFT 16 #define IRQ_STAT0_TXSHIFT 0 #define USBCTRL_IRQ_STAT1 0x34 #define IRQ_STAT1_DRVVBUS (1 << 8) #define USBCTRL_INTEN_SET0 0x38 #define USBCTRL_INTEN_SET1 0x3C #define USBCTRL_INTEN_USB_ALL 0x1ff #define USBCTRL_INTEN_USB_SOF (1 << 3) #define USBCTRL_INTEN_CLR0 0x40 #define USBCTRL_INTEN_CLR1 0x44 #define USBCTRL_UTMI 0xE0 #define USBCTRL_UTMI_FSDATAEXT (1 << 1) #define USBCTRL_MODE 0xE8 #define USBCTRL_MODE_IDDIG (1 << 8) #define USBCTRL_MODE_IDDIGMUX (1 << 7) /* USBSS resource + 2 MUSB ports */ #define RES_USBSS 0 #define RES_USBCTRL(i) (3*i+1) #define RES_USBPHY(i) (3*i+2) #define RES_USBCORE(i) (3*i+3) #define USB_WRITE4(sc, idx, reg, val) do { \ bus_write_4((sc)->sc_mem_res[idx], (reg), (val)); \ } while (0) #define USB_READ4(sc, idx, reg) bus_read_4((sc)->sc_mem_res[idx], (reg)) #define USBSS_WRITE4(sc, reg, val) \ USB_WRITE4((sc), RES_USBSS, (reg), (val)) #define USBSS_READ4(sc, reg) \ USB_READ4((sc), RES_USBSS, (reg)) #define USBCTRL_WRITE4(sc, unit, reg, val) \ USB_WRITE4((sc), RES_USBCTRL(unit), (reg), (val)) #define USBCTRL_READ4(sc, unit, reg) \ USB_READ4((sc), RES_USBCTRL(unit), (reg)) #define USBPHY_WRITE4(sc, unit, reg, val) \ USB_WRITE4((sc), RES_USBPHY(unit), (reg), (val)) #define USBPHY_READ4(sc, unit, reg) \ USB_READ4((sc), RES_USBPHY(unit), (reg)) static struct resource_spec am335x_musbotg_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_MEMORY, 3, RF_ACTIVE }, { SYS_RES_MEMORY, 4, RF_ACTIVE }, { SYS_RES_MEMORY, 5, RF_ACTIVE }, { SYS_RES_MEMORY, 6, RF_ACTIVE }, { -1, 0, 0 } }; static struct resource_spec am335x_musbotg_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { -1, 0, 0 } }; #ifdef USB_DEBUG static int usbssdebug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, am335x_usbss, CTLFLAG_RW, 0, "AM335x USBSS"); SYSCTL_INT(_hw_usb_am335x_usbss, OID_AUTO, debug, CTLFLAG_RW, &usbssdebug, 0, "Debug level"); #endif static device_probe_t musbotg_probe; static device_attach_t musbotg_attach; static device_detach_t musbotg_detach; struct musbotg_super_softc { struct musbotg_softc sc_otg[AM335X_USB_PORTS]; struct resource *sc_mem_res[AM335X_USB_PORTS*3+1]; struct resource *sc_irq_res[AM335X_USB_PORTS+1]; void *sc_intr_hdl; }; static void musbotg_vbus_poll(struct musbotg_super_softc *sc, int port) { uint32_t stat; if (sc->sc_otg[port].sc_mode == MUSB2_DEVICE_MODE) musbotg_vbus_interrupt(&sc->sc_otg[port], 1); else { stat = USBCTRL_READ4(sc, port, USBCTRL_STAT); musbotg_vbus_interrupt(&sc->sc_otg[port], stat & 1); } } /* * Arg to musbotg_clocks_on and musbot_clocks_off is * a uint32_t * pointing to the SCM register offset. */ static uint32_t USB_CTRL[] = {SCM_USB_CTRL0, SCM_USB_CTRL1}; static void musbotg_clocks_on(void *arg) { uint32_t c, reg = *(uint32_t *)arg; ti_scm_reg_read_4(reg, &c); c &= ~3; /* Enable power */ c |= 1 << 19; /* VBUS detect enable */ c |= 1 << 20; /* Session end enable */ ti_scm_reg_write_4(reg, c); } static void musbotg_clocks_off(void *arg) { uint32_t c, reg = *(uint32_t *)arg; /* Disable power to PHY */ ti_scm_reg_read_4(reg, &c); ti_scm_reg_write_4(reg, c | 3); } static void musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on) { struct musbotg_super_softc *ssc = sc->sc_platform_data; uint32_t epmask; epmask = ((1 << ep) << IRQ_STAT0_RXSHIFT); epmask |= ((1 << ep) << IRQ_STAT0_TXSHIFT); if (on) USBCTRL_WRITE4(ssc, sc->sc_id, USBCTRL_INTEN_SET0, epmask); else USBCTRL_WRITE4(ssc, sc->sc_id, USBCTRL_INTEN_CLR0, epmask); } static void musbotg_usbss_interrupt(void *arg) { panic("USBSS real interrupt"); } static void musbotg_wrapper_interrupt(void *arg) { struct musbotg_softc *sc = arg; struct musbotg_super_softc *ssc = sc->sc_platform_data; uint32_t stat, stat0, stat1; stat = USBCTRL_READ4(ssc, sc->sc_id, USBCTRL_STAT); stat0 = USBCTRL_READ4(ssc, sc->sc_id, USBCTRL_IRQ_STAT0); stat1 = USBCTRL_READ4(ssc, sc->sc_id, USBCTRL_IRQ_STAT1); if (stat0) USBCTRL_WRITE4(ssc, sc->sc_id, USBCTRL_IRQ_STAT0, stat0); if (stat1) USBCTRL_WRITE4(ssc, sc->sc_id, USBCTRL_IRQ_STAT1, stat1); DPRINTFN(4, "port%d: stat0=%08x stat1=%08x, stat=%08x\n", sc->sc_id, stat0, stat1, stat); if (stat1 & IRQ_STAT1_DRVVBUS) musbotg_vbus_interrupt(sc, stat & 1); musbotg_interrupt(arg, ((stat0 >> 16) & 0xffff), stat0 & 0xffff, stat1 & 0xff); } static int musbotg_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,musb-am33xx")) return (ENXIO); device_set_desc(dev, "TI AM33xx integrated USB OTG controller"); return (BUS_PROBE_DEFAULT); } static int musbotg_attach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); int err; int i; uint32_t rev, reg; /* Request the memory resources */ err = bus_alloc_resources(dev, am335x_musbotg_mem_spec, sc->sc_mem_res); if (err) { device_printf(dev, "Error: could not allocate mem resources\n"); return (ENXIO); } /* Request the IRQ resources */ err = bus_alloc_resources(dev, am335x_musbotg_irq_spec, sc->sc_irq_res); if (err) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* Enable device clocks. */ ti_prcm_clk_enable(MUSB0_CLK); /* * Reset USBSS, USB0 and USB1. * The registers of USB subsystem must not be accessed while the * reset pulse is active (200ns). */ USBSS_WRITE4(sc, USBSS_SYSCONFIG, USBSS_SYSCONFIG_SRESET); DELAY(100); i = 10; while (USBSS_READ4(sc, USBSS_SYSCONFIG) & USBSS_SYSCONFIG_SRESET) { DELAY(100); if (i-- == 0) { device_printf(dev, "reset timeout.\n"); return (ENXIO); } } /* Read the module revision. */ rev = USBSS_READ4(sc, USBSS_REVREG); device_printf(dev, "TI AM335X USBSS v%d.%d.%d\n", (rev >> 8) & 7, (rev >> 6) & 3, rev & 63); err = bus_setup_intr(dev, sc->sc_irq_res[0], INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)musbotg_usbss_interrupt, sc, &sc->sc_intr_hdl); if (err) { sc->sc_intr_hdl = NULL; device_printf(dev, "Failed to setup USBSS interrupt\n"); goto error; } for (i = 0; i < AM335X_USB_PORTS; i++) { /* setup MUSB OTG USB controller interface softc */ sc->sc_otg[i].sc_clocks_on = &musbotg_clocks_on; sc->sc_otg[i].sc_clocks_off = &musbotg_clocks_off; sc->sc_otg[i].sc_clocks_arg = &USB_CTRL[i]; sc->sc_otg[i].sc_ep_int_set = musbotg_ep_int_set; /* initialise some bus fields */ sc->sc_otg[i].sc_bus.parent = dev; sc->sc_otg[i].sc_bus.devices = sc->sc_otg[i].sc_devices; sc->sc_otg[i].sc_bus.devices_max = MUSB2_MAX_DEVICES; + sc->sc_otg[i].sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_otg[i].sc_bus, USB_GET_DMA_TAG(dev), NULL)) { device_printf(dev, "Failed allocate bus mem for musb%d\n", i); return (ENOMEM); } sc->sc_otg[i].sc_io_res = sc->sc_mem_res[RES_USBCORE(i)]; sc->sc_otg[i].sc_io_tag = rman_get_bustag(sc->sc_otg[i].sc_io_res); sc->sc_otg[i].sc_io_hdl = rman_get_bushandle(sc->sc_otg[i].sc_io_res); sc->sc_otg[i].sc_io_size = rman_get_size(sc->sc_otg[i].sc_io_res); sc->sc_otg[i].sc_irq_res = sc->sc_irq_res[i+1]; sc->sc_otg[i].sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_otg[i].sc_bus.bdev)) { device_printf(dev, "No busdev for musb%d\n", i); goto error; } device_set_ivars(sc->sc_otg[i].sc_bus.bdev, &sc->sc_otg[i].sc_bus); err = bus_setup_intr(dev, sc->sc_otg[i].sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)musbotg_wrapper_interrupt, &sc->sc_otg[i], &sc->sc_otg[i].sc_intr_hdl); if (err) { sc->sc_otg[i].sc_intr_hdl = NULL; device_printf(dev, "Failed to setup interrupt for musb%d\n", i); goto error; } sc->sc_otg[i].sc_id = i; sc->sc_otg[i].sc_platform_data = sc; if (i == 0) sc->sc_otg[i].sc_mode = MUSB2_DEVICE_MODE; else sc->sc_otg[i].sc_mode = MUSB2_HOST_MODE; /* * software-controlled function */ if (sc->sc_otg[i].sc_mode == MUSB2_HOST_MODE) { reg = USBCTRL_READ4(sc, i, USBCTRL_MODE); reg |= USBCTRL_MODE_IDDIGMUX; reg &= ~USBCTRL_MODE_IDDIG; USBCTRL_WRITE4(sc, i, USBCTRL_MODE, reg); USBCTRL_WRITE4(sc, i, USBCTRL_UTMI, USBCTRL_UTMI_FSDATAEXT); } else { reg = USBCTRL_READ4(sc, i, USBCTRL_MODE); reg |= USBCTRL_MODE_IDDIGMUX; reg |= USBCTRL_MODE_IDDIG; USBCTRL_WRITE4(sc, i, USBCTRL_MODE, reg); } reg = USBCTRL_INTEN_USB_ALL & ~USBCTRL_INTEN_USB_SOF; USBCTRL_WRITE4(sc, i, USBCTRL_INTEN_SET1, reg); USBCTRL_WRITE4(sc, i, USBCTRL_INTEN_CLR0, 0xffffffff); err = musbotg_init(&sc->sc_otg[i]); if (!err) err = device_probe_and_attach(sc->sc_otg[i].sc_bus.bdev); if (err) goto error; /* poll VBUS one time */ musbotg_vbus_poll(sc, i); } return (0); error: musbotg_detach(dev); return (ENXIO); } static int musbotg_detach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); device_t bdev; int err; int i; for (i = 0; i < AM335X_USB_PORTS; i++) { if (sc->sc_otg[i].sc_bus.bdev) { bdev = sc->sc_otg[i].sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } if (sc->sc_otg[i].sc_irq_res && sc->sc_otg[i].sc_intr_hdl) { /* * only call musbotg_uninit() after musbotg_init() */ musbotg_uninit(&sc->sc_otg[i]); err = bus_teardown_intr(dev, sc->sc_otg[i].sc_irq_res, sc->sc_otg[i].sc_intr_hdl); sc->sc_otg[i].sc_intr_hdl = NULL; } usb_bus_mem_free_all(&sc->sc_otg[i].sc_bus, NULL); } if (sc->sc_intr_hdl) { bus_teardown_intr(dev, sc->sc_irq_res[0], sc->sc_intr_hdl); sc->sc_intr_hdl = NULL; } /* Free resources if any */ if (sc->sc_mem_res[0]) bus_release_resources(dev, am335x_musbotg_mem_spec, sc->sc_mem_res); if (sc->sc_irq_res[0]) bus_release_resources(dev, am335x_musbotg_irq_spec, sc->sc_irq_res); /* during module unload there are lots of children leftover */ device_delete_children(dev); return (0); } static device_method_t musbotg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, musbotg_probe), DEVMETHOD(device_attach, musbotg_attach), DEVMETHOD(device_detach, musbotg_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t musbotg_driver = { .name = "musbotg", .methods = musbotg_methods, .size = sizeof(struct musbotg_super_softc), }; static devclass_t musbotg_devclass; DRIVER_MODULE(musbotg, simplebus, musbotg_driver, musbotg_devclass, 0, 0); MODULE_DEPEND(musbotg, usb, 1, 1, 1); Index: head/sys/arm/ti/usb/omap_ehci.c =================================================================== --- head/sys/arm/ti/usb/omap_ehci.c (revision 276716) +++ head/sys/arm/ti/usb/omap_ehci.c (revision 276717) @@ -1,1022 +1,1023 @@ /*- * Copyright (c) 2011 * Ben Gray . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /** * Driver for the High Speed USB EHCI module on the TI OMAP3530 SoC. * * WARNING: I've only tried this driver on a limited number of USB peripherals, * it is still very raw and bound to have numerous bugs in it. * * This driver is based on the FreeBSD IXP4xx EHCI driver with a lot of the * setup sequence coming from the Linux community and their EHCI driver for * OMAP. Without these as a base I don't think I would have been able to get * this driver working. * * The driver only contains the EHCI parts, the module also supports OHCI and * USB on-the-go (OTG), currently neither are supported. * * CAUTION: This driver was written to run on the beaglebaord dev board, so I * have made some assumptions about the type of PHY used and some of the other * settings. Bare that in mind if you intend to use this driver on another * platform. * * NOTE: This module uses a few different clocks, one being a 60Mhz clock for * the TTL part of the module. This clock is derived from DPPL5 which must be * configured prior to loading this driver - it is not configured by the * bootloader. It took me a long time to figure this out, and caused much * frustration. This PLL is now setup in the timer/clocks part of the BSP, * check out the omap_prcm_setup_dpll5() function in omap_prcm.c for more info. * */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" struct omap_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ device_t sc_dev; device_t sc_gpio_dev; /* TLL register set */ struct resource* tll_mem_res; /* UHH register set */ struct resource* uhh_mem_res; /* The revision of the HS USB HOST read from UHH_REVISION */ uint32_t ehci_rev; /* The following details are provided by conf hints */ int port_mode[3]; int phy_reset[3]; int reset_gpio_pin[3]; }; static device_attach_t omap_ehci_attach; static device_detach_t omap_ehci_detach; static device_shutdown_t omap_ehci_shutdown; static device_suspend_t omap_ehci_suspend; static device_resume_t omap_ehci_resume; /** * omap_tll_read_4 - read a 32-bit value from the USBTLL registers * omap_tll_write_4 - write a 32-bit value from the USBTLL registers * omap_tll_readb - read an 8-bit value from the USBTLL registers * omap_tll_writeb - write an 8-bit value from the USBTLL registers * @sc: omap ehci device context * @off: byte offset within the register set to read from * @val: the value to write into the register * * * LOCKING: * None * * RETURNS: * nothing in case of write function, if read function returns the value read. */ static inline uint32_t omap_tll_read_4(struct omap_ehci_softc *sc, bus_size_t off) { return bus_read_4(sc->tll_mem_res, off); } static inline void omap_tll_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->tll_mem_res, off, val); } static inline uint8_t omap_tll_readb(struct omap_ehci_softc *sc, bus_size_t off) { return bus_read_1(sc->tll_mem_res, off); } static inline void omap_tll_writeb(struct omap_ehci_softc *sc, bus_size_t off, uint8_t val) { bus_write_1(sc->tll_mem_res, off, val); } /** * omap_ehci_read_4 - read a 32-bit value from the EHCI registers * omap_ehci_write_4 - write a 32-bit value from the EHCI registers * @sc: omap ehci device context * @off: byte offset within the register set to read from * @val: the value to write into the register * * * LOCKING: * None * * RETURNS: * nothing in case of write function, if read function returns the value read. */ static inline uint32_t omap_ehci_read_4(struct omap_ehci_softc *sc, bus_size_t off) { return (bus_read_4(sc->base.sc_io_res, off)); } static inline void omap_ehci_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->base.sc_io_res, off, val); } /** * omap_uhh_read_4 - read a 32-bit value from the UHH registers * omap_uhh_write_4 - write a 32-bit value from the UHH registers * @sc: omap ehci device context * @off: byte offset within the register set to read from * @val: the value to write into the register * * * LOCKING: * None * * RETURNS: * nothing in case of write function, if read function returns the value read. */ static inline uint32_t omap_uhh_read_4(struct omap_ehci_softc *sc, bus_size_t off) { return bus_read_4(sc->uhh_mem_res, off); } static inline void omap_uhh_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->uhh_mem_res, off, val); } /** * omap_ehci_utmi_init - initialises the UTMI part of the controller * @isc: omap ehci device context * * * * LOCKING: * none * * RETURNS: * nothing */ static void omap_ehci_utmi_init(struct omap_ehci_softc *isc, unsigned int en_mask) { unsigned int i; uint32_t reg; /* There are 3 TLL channels, one per USB controller so set them all up the * same, SDR mode, bit stuffing and no autoidle. */ for (i=0; i<3; i++) { reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); reg &= ~(TLL_CHANNEL_CONF_UTMIAUTOIDLE | TLL_CHANNEL_CONF_ULPINOBITSTUFF | TLL_CHANNEL_CONF_ULPIDDRMODE); omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); } /* Program the common TLL register */ reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_SHARED_CONF); reg &= ~( TLL_SHARED_CONF_USB_90D_DDR_EN | TLL_SHARED_CONF_USB_DIVRATIO_MASK); reg |= ( TLL_SHARED_CONF_FCLK_IS_ON | TLL_SHARED_CONF_USB_DIVRATIO_2 | TLL_SHARED_CONF_USB_180D_SDR_EN); omap_tll_write_4(isc, OMAP_USBTLL_TLL_SHARED_CONF, reg); /* Enable channels now */ for (i = 0; i < 3; i++) { reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); /* Enable only the reg that is needed */ if ((en_mask & (1 << i)) == 0) continue; reg |= TLL_CHANNEL_CONF_CHANEN; omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); } } /** * omap_ehci_soft_phy_reset - resets the phy using the reset command * @isc: omap ehci device context * @port: port to send the reset over * * * LOCKING: * none * * RETURNS: * nothing */ static void omap_ehci_soft_phy_reset(struct omap_ehci_softc *isc, unsigned int port) { unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); uint32_t reg; reg = ULPI_FUNC_CTRL_RESET /* FUNCTION_CTRL_SET register */ | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT) /* Write */ | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT) /* PORTn */ | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT) /* start ULPI access*/ | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT); omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG05_ULPI, reg); /* Wait for ULPI access completion */ while ((omap_ehci_read_4(isc, OMAP_USBHOST_INSNREG05_ULPI) & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) { /* Sleep for a tick */ pause("USBPHY_RESET", 1); if (timeout-- == 0) { device_printf(isc->sc_dev, "PHY reset operation timed out\n"); break; } } } /** * omap_ehci_init - initialises the USB host EHCI controller * @isc: omap ehci device context * * This initialisation routine is quite heavily based on the work done by the * OMAP Linux team (for which I thank them very much). The init sequence is * almost identical, diverging only for the FreeBSD specifics. * * LOCKING: * none * * RETURNS: * 0 on success, a negative error code on failure. */ static int omap_ehci_init(struct omap_ehci_softc *isc) { unsigned long timeout; int ret = 0; uint8_t tll_ch_mask = 0; uint32_t reg = 0; int reset_performed = 0; int i; device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); /* Enable Clocks for high speed USBHOST */ ti_prcm_clk_enable(USBHSHOST_CLK); /* Hold the PHY in reset while configuring */ for (int i = 0; i < 3; i++) { if (isc->phy_reset[i]) { /* Configure the GPIO to drive low (hold in reset) */ if ((isc->reset_gpio_pin[i] != -1) && (isc->sc_gpio_dev != NULL)) { GPIO_PIN_SETFLAGS(isc->sc_gpio_dev, isc->reset_gpio_pin[i], GPIO_PIN_OUTPUT); GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[i], GPIO_PIN_LOW); reset_performed = 1; } } } /* Hold the PHY in RESET for enough time till DIR is high */ if (reset_performed) DELAY(10); /* Read the UHH revision */ isc->ehci_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION); device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->ehci_rev); /* Initilise the low level interface module(s) */ if (isc->ehci_rev == OMAP_EHCI_REV1) { /* Enable the USB TLL */ ti_prcm_clk_enable(USBTLL_CLK); /* Perform TLL soft reset, and wait until reset is complete */ omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET); /* Set the timeout to 100ms*/ timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); /* Wait for TLL reset to complete */ while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & TLL_SYSSTATUS_RESETDONE) == 0x00) { /* Sleep for a tick */ pause("USBRESET", 1); if (timeout-- == 0) { device_printf(isc->sc_dev, "TLL reset operation timed out\n"); ret = EINVAL; goto err_sys_status; } } device_printf(isc->sc_dev, "TLL RESET DONE\n"); /* CLOCKACTIVITY = 1 : OCP-derived internal clocks ON during idle * SIDLEMODE = 2 : Smart-idle mode. Sidleack asserted after Idlereq * assertion when no more activity on the USB. * ENAWAKEUP = 1 : Wakeup generation enabled */ omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP | TLL_SYSCONFIG_AUTOIDLE | TLL_SYSCONFIG_SIDLE_SMART_IDLE | TLL_SYSCONFIG_CACTIVITY); } else if (isc->ehci_rev == OMAP_EHCI_REV2) { /* For OMAP44xx devices you have to enable the per-port clocks: * PHY_MODE - External ULPI clock * TTL_MODE - Internal UTMI clock * HSIC_MODE - Internal 480Mhz and 60Mhz clocks */ if (isc->ehci_rev == OMAP_EHCI_REV2) { if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) { ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK); ti_prcm_clk_enable(USBP1_PHY_CLK); } else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) ti_prcm_clk_enable(USBP1_UTMI_CLK); else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) ti_prcm_clk_enable(USBP1_HSIC_CLK); if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) { ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK); ti_prcm_clk_enable(USBP2_PHY_CLK); } else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) ti_prcm_clk_enable(USBP2_UTMI_CLK); else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) ti_prcm_clk_enable(USBP2_HSIC_CLK); } } /* Put UHH in SmartIdle/SmartStandby mode */ reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG); if (isc->ehci_rev == OMAP_EHCI_REV1) { reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | UHH_SYSCONFIG_MIDLEMODE_MASK); reg |= (UHH_SYSCONFIG_ENAWAKEUP | UHH_SYSCONFIG_AUTOIDLE | UHH_SYSCONFIG_CLOCKACTIVITY | UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); } else if (isc->ehci_rev == OMAP_EHCI_REV2) { reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; } omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG); /* Setup ULPI bypass and burst configurations */ reg |= (UHH_HOSTCONFIG_ENA_INCR4 | UHH_HOSTCONFIG_ENA_INCR8 | UHH_HOSTCONFIG_ENA_INCR16); reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; if (isc->ehci_rev == OMAP_EHCI_REV1) { if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; /* Bypass the TLL module for PHY mode operation */ if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; else reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; } else if (isc->ehci_rev == OMAP_EHCI_REV2) { reg |= UHH_HOSTCONFIG_APP_START_CLK; /* Clear port mode fields for PHY mode*/ reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; } omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg); device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg); /* I found the code and comments in the Linux EHCI driver - thanks guys :) * * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared * (for example when we do ehci_bus_suspend). This breaks suspend-resume if * the root-hub is allowed to suspend. Writing 1 to this undocumented * register bit disables this feature and restores normal behavior." */ #if 0 omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG04, OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); #endif /* If any of the ports are configured in TLL mode, enable them */ if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= 0x1; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= 0x2; if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= 0x4; /* Enable UTMI mode for required TLL channels */ omap_ehci_utmi_init(isc, tll_ch_mask); } /* Release the PHY reset signal now we have configured everything */ if (reset_performed) { /* Delay for 10ms */ DELAY(10000); for (i = 0; i < 3; i++) { /* Release reset */ if (isc->phy_reset[i] && (isc->reset_gpio_pin[i] != -1) && (isc->sc_gpio_dev != NULL)) { GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[i], GPIO_PIN_HIGH); } } } /* Set the interrupt threshold control, it controls the maximum rate at * which the host controller issues interrupts. We set it to 1 microframe * at startup - the default is 8 mircoframes (equates to 1ms). */ reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD); reg &= 0xff00ffff; reg |= (1 << 16); omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg); /* Soft reset the PHY using PHY reset command over ULPI */ if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(isc, 0); if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(isc, 1); return(0); err_sys_status: /* Disable the TLL clocks */ ti_prcm_clk_disable(USBTLL_CLK); /* Disable Clocks for USBHOST */ ti_prcm_clk_disable(USBHSHOST_CLK); return(ret); } /** * omap_ehci_fini - shutdown the EHCI controller * @isc: omap ehci device context * * * * LOCKING: * none * * RETURNS: * 0 on success, a negative error code on failure. */ static void omap_ehci_fini(struct omap_ehci_softc *isc) { unsigned long timeout; device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n"); /* Set the timeout */ if (hz < 10) timeout = 1; else timeout = (100 * hz) / 1000; /* Reset the UHH, OHCI and EHCI modules */ omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) { /* Sleep for a tick */ pause("USBRESET", 1); if (timeout-- == 0) { device_printf(isc->sc_dev, "operation timed out\n"); break; } } /* Set the timeout */ if (hz < 10) timeout = 1; else timeout = (100 * hz) / 1000; /* Reset the TLL module */ omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, 0x0002); while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & (0x01)) == 0x00) { /* Sleep for a tick */ pause("USBRESET", 1); if (timeout-- == 0) { device_printf(isc->sc_dev, "operation timed out\n"); break; } } /* Disable functional and interface clocks for the TLL and HOST modules */ ti_prcm_clk_disable(USBTLL_CLK); ti_prcm_clk_disable(USBHSHOST_CLK); device_printf(isc->sc_dev, "Clock to USB host has been disabled\n"); } /** * omap_ehci_suspend - suspends the bus * @dev: omap ehci device * * Effectively boilerplate EHCI suspend code. * * TODO: There is a lot more we could do here - i.e. force the controller into * idle mode and disable all the clocks for start. * * LOCKING: * none * * RETURNS: * 0 on success or a positive error code */ static int omap_ehci_suspend(device_t dev) { int err; err = bus_generic_suspend(dev); if (err) return (err); return (0); } /** * omap_ehci_resume - resumes a suspended bus * @dev: omap ehci device * * Effectively boilerplate EHCI resume code. * * LOCKING: * none * * RETURNS: * 0 on success or a positive error code on failure */ static int omap_ehci_resume(device_t dev) { bus_generic_resume(dev); return (0); } /** * omap_ehci_shutdown - starts the given command * @dev: * * Effectively boilerplate EHCI shutdown code. * * LOCKING: * none. * * RETURNS: * 0 on success or a positive error code on failure */ static int omap_ehci_shutdown(device_t dev) { int err; err = bus_generic_shutdown(dev); if (err) return (err); return (0); } /** * omap_ehci_probe - starts the given command * @dev: * * Effectively boilerplate EHCI resume code. * * LOCKING: * Caller should be holding the OMAP3_MMC lock. * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ static int omap_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,usb-ehci")) return (ENXIO); device_set_desc(dev, OMAP_EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } /** * omap_ehci_attach - driver entry point, sets up the ECHI controller/driver * @dev: the new device handle * * Sets up bus spaces, interrupt handles, etc for the EHCI controller. It also * parses the resource hints and calls omap_ehci_init() to initialise the * H/W. * * LOCKING: * none * * RETURNS: * 0 on success or a positive error code on failure. */ static int omap_ehci_attach(device_t dev) { struct omap_ehci_softc *isc = device_get_softc(dev); phandle_t node; /* 3 ports with 3 cells per port */ pcell_t phyconf[3 * 3]; pcell_t *phyconf_ptr; ehci_softc_t *sc = &isc->base; int err; int rid; int len, tuple_size; int i; /* initialise some bus fields */ sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; - + sc->sc_bus.dma_bits = 32; + /* save the device */ isc->sc_dev = dev; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) { return (ENOMEM); } /* When the EHCI driver is added to the tree it is expected that 3 * memory resources and 1 interrupt resource is assigned. The memory * resources should be: * 0 => EHCI register range * 1 => UHH register range * 2 => TLL register range * * The interrupt resource is just the single interupt for the controller. */ /* Allocate resource for the EHCI register set */ rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(dev, "Error: Could not map EHCI memory\n"); goto error; } /* Request an interrupt resource */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Error: could not allocate irq\n"); goto error; } /* Allocate resource for the UHH register set */ rid = 1; isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!isc->uhh_mem_res) { device_printf(dev, "Error: Could not map UHH memory\n"); goto error; } /* Allocate resource for the TLL register set */ rid = 2; isc->tll_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!isc->tll_mem_res) { device_printf(dev, "Error: Could not map TLL memory\n"); goto error; } /* Add this device as a child of the USBus device */ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Error: could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, OMAP_EHCI_HC_DEVSTR); /* Set the vendor name */ sprintf(sc->sc_vendor, "Texas Instruments"); /* Get the GPIO device, we may need this if the driver needs to toggle * some pins for external PHY resets. */ isc->sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (isc->sc_gpio_dev == NULL) { device_printf(dev, "Error: failed to get the GPIO device\n"); goto error; } /* Set the defaults for the hints */ for (i = 0; i < 3; i++) { isc->phy_reset[i] = 0; isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; isc->reset_gpio_pin[i] = -1; } tuple_size = sizeof(pcell_t) * 3; node = ofw_bus_get_node(dev); len = OF_getprop(node, "phy-config", phyconf, sizeof(phyconf)); if (len > 0) { if (len % tuple_size) goto error; if ((len / tuple_size) != 3) goto error; phyconf_ptr = phyconf; for (i = 0; i < 3; i++) { isc->port_mode[i] = fdt32_to_cpu(*phyconf_ptr); isc->phy_reset[i] = fdt32_to_cpu(*(phyconf_ptr + 1)); isc->reset_gpio_pin[i] = fdt32_to_cpu(*(phyconf_ptr + 2)); phyconf_ptr += 3; } } /* Initialise the ECHI registers */ err = omap_ehci_init(isc); if (err) { device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); goto error; } /* Set the tag and size of the register set in the EHCI context */ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); /* Setup the interrupt */ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Error: could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* Finally we are ready to kick off the ECHI host controller */ err = ehci_init(sc); if (err == 0) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(dev, "Error: USB init failed err=%d\n", err); goto error; } return (0); error: omap_ehci_detach(dev); return (ENXIO); } /** * omap_ehci_detach - detach the device and cleanup the driver * @dev: device handle * * Clean-up routine where everything initialised in omap_ehci_attach is * freed and cleaned up. This function calls omap_ehci_fini() to shutdown * the on-chip module. * * LOCKING: * none * * RETURNS: * Always returns 0 (success). */ static int omap_ehci_detach(device_t dev) { struct omap_ehci_softc *isc = device_get_softc(dev); ehci_softc_t *sc = &isc->base; device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); /* * disable interrupts that might have been switched on in ehci_init */ if (sc->sc_io_res) { EWRITE4(sc, EHCI_USBINTR, 0); } if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); if (err) device_printf(dev, "Error: could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } /* Free the resources stored in the base EHCI handler */ if (sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } /* Release the other register set memory maps */ if (isc->tll_mem_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->tll_mem_res); isc->tll_mem_res = NULL; } if (isc->uhh_mem_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res); isc->uhh_mem_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); omap_ehci_fini(isc); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, omap_ehci_probe), DEVMETHOD(device_attach, omap_ehci_attach), DEVMETHOD(device_detach, omap_ehci_detach), DEVMETHOD(device_suspend, omap_ehci_suspend), DEVMETHOD(device_resume, omap_ehci_resume), DEVMETHOD(device_shutdown, omap_ehci_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(struct omap_ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); Index: head/sys/arm/xilinx/zy7_ehci.c =================================================================== --- head/sys/arm/xilinx/zy7_ehci.c (revision 276716) +++ head/sys/arm/xilinx/zy7_ehci.c (revision 276717) @@ -1,365 +1,366 @@ /*- * Copyright (c) 2012-2013 Thomas Skibo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * A host-controller driver for Zynq-7000's USB OTG controller. * * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. * (v1.4) November 16, 2012. Xilinx doc UG585. Ch. 15 covers the USB * controller and register definitions are in appendix B.34. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Register definitions. */ #define ZY7_USB_ID 0x0000 #define ZY7_USB_HWGENERAL 0x0004 #define ZY7_USB_HWHOST 0x0008 #define ZY7_USB_HWDEVICE 0x000c #define ZY7_USB_HWTXBUF 0x0010 #define ZY7_USB_HWRXBUF 0x0014 #define ZY7_USB_GPTIMER0LD 0x0080 #define ZY7_USB_GPTIMER0CTRL 0x0084 #define ZY7_USB_GPTIMER1LD 0x0088 #define ZY7_USB_GPTIMER1CTRL 0x008c #define ZY7_USB_SBUSCFG 0x0090 #define ZY7_USB_CAPLENGTH_HCIVERSION 0x0100 #define ZY7_USB_HCSPARAMS 0x0104 #define ZY7_USB_HCCPARAMS 0x0108 #define ZY7_USB_DCIVERSION 0x0120 #define ZY7_USB_DCCPARAMS 0x0124 #define ZY7_USB_USBCMD 0x0140 #define ZY7_USB_USBSTS 0x0144 #define ZY7_USB_USBINTR 0x0148 #define ZY7_USB_FRINDEX 0x014c #define ZY7_USB_PERIODICLISTBASE_DEICEADDR 0x0154 #define ZY7_USB_ASYNCLISTADDR_ENDPOINTLISTADDR 0x0158 #define ZY7_USB_TTCTRL 0x015c #define ZY7_USB_BURSTSIZE 0x0160 #define ZY7_USB_TXFILLTUNING 0x0164 #define ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT 16 #define ZY7_USB_TXFILLTUNING_TXFIFOTHRES_MASK (0x3f<<16) #define ZY7_USB_TXTFILLTUNING 0x0168 #define ZY7_USB_IC_USB 0x016c #define ZY7_USB_ULPI_VIEWPORT 0x0170 #define ZY7_USB_ULPI_VIEWPORT_WU (1<<31) #define ZY7_USB_ULPI_VIEWPORT_RUN (1<<30) #define ZY7_USB_ULPI_VIEWPORT_RW (1<<29) #define ZY7_USB_ULPI_VIEWPORT_SS (1<<27) #define ZY7_USB_ULPI_VIEWPORT_PORT_MASK (7<<24) #define ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT 24 #define ZY7_USB_ULPI_VIEWPORT_ADDR_MASK (0xff<<16) #define ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT 16 #define ZY7_USB_ULPI_VIEWPORT_DATARD_MASK (0xff<<8) #define ZY7_USB_ULPI_VIEWPORT_DATARD_SHIFT 8 #define ZY7_USB_ULPI_VIEWPORT_DATAWR_MASK (0xff<<0) #define ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT 0 #define ZY7_USB_ENDPTNAK 0x0178 #define ZY7_USB_ENDPTNAKEN 0x017c #define ZY7_USB_CONFIGFLAG 0x0180 #define ZY7_USB_PORTSC(n) (0x0180+4*(n)) #define ZY7_USB_PORTSC_PTS_MASK (3<<30) #define ZY7_USB_PORTSC_PTS_SHIFT 30 #define ZY7_USB_PORTSC_PTS_UTMI (0<<30) #define ZY7_USB_PORTSC_PTS_ULPI (2<<30) #define ZY7_USB_PORTSC_PTS_SERIAL (3<<30) #define ZY7_USB_PORTSC_PTW (1<<28) #define ZY7_USB_PORTSC_PTS2 (1<<25) #define ZY7_USB_OTGSC 0x01a4 #define ZY7_USB_USBMODE 0x01a8 #define ZY7_USB_ENDPTSETUPSTAT 0x01ac #define ZY7_USB_ENDPTPRIME 0x01b0 #define ZY7_USB_ENDPTFLUSH 0x01b4 #define ZY7_USB_ENDPTSTAT 0x01b8 #define ZY7_USB_ENDPTCOMPLETE 0x01bc #define ZY7_USB_ENDPTCTRL(n) (0x01c0+4*(n)) #define EHCI_REG_OFFSET ZY7_USB_CAPLENGTH_HCIVERSION #define EHCI_REG_SIZE 0x100 static int zy7_phy_config(device_t dev, bus_space_tag_t io_tag, bus_space_handle_t bsh) { phandle_t node; char buf[64]; uint32_t portsc; int tries; node = ofw_bus_get_node(dev); if (OF_getprop(node, "phy_type", buf, sizeof(buf)) > 0) { portsc = bus_space_read_4(io_tag, bsh, ZY7_USB_PORTSC(1)); portsc &= ~(ZY7_USB_PORTSC_PTS_MASK | ZY7_USB_PORTSC_PTW | ZY7_USB_PORTSC_PTS2); if (strcmp(buf,"ulpi") == 0) portsc |= ZY7_USB_PORTSC_PTS_ULPI; else if (strcmp(buf,"utmi") == 0) portsc |= ZY7_USB_PORTSC_PTS_UTMI; else if (strcmp(buf,"utmi-wide") == 0) portsc |= (ZY7_USB_PORTSC_PTS_UTMI | ZY7_USB_PORTSC_PTW); else if (strcmp(buf, "serial") == 0) portsc |= ZY7_USB_PORTSC_PTS_SERIAL; bus_space_write_4(io_tag, bsh, ZY7_USB_PORTSC(1), portsc); } if (OF_getprop(node, "phy_vbus_ext", buf, sizeof(buf)) >= 0) { /* Tell PHY that VBUS is supplied externally. */ bus_space_write_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT, ZY7_USB_ULPI_VIEWPORT_RUN | ZY7_USB_ULPI_VIEWPORT_RW | (0 << ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT) | (0x0b << ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT) | (0x60 << ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT) ); tries = 100; while ((bus_space_read_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT) & ZY7_USB_ULPI_VIEWPORT_RUN) != 0) { if (--tries < 0) return (-1); DELAY(1); } } return (0); } static int zy7_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "xlnx,zy7_ehci")) return (ENXIO); device_set_desc(dev, "Zynq-7000 EHCI USB 2.0 controller"); return (0); } static int zy7_ehci_detach(device_t dev); static int zy7_ehci_attach(device_t dev) { ehci_softc_t *sc = device_get_softc(dev); bus_space_handle_t bsh; int err, rid; /* initialize some bus fields */ sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) return (ENOMEM); /* Allocate memory. */ rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_io_res == NULL) { device_printf(dev, "Can't allocate memory"); zy7_ehci_detach(dev); return (ENOMEM); } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); bsh = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = EHCI_REG_SIZE; if (bus_space_subregion(sc->sc_io_tag, bsh, EHCI_REG_OFFSET, sc->sc_io_size, &sc->sc_io_hdl) != 0) panic("%s: unable to subregion USB host registers", device_get_name(dev)); /* Allocate IRQ. */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Can't allocate IRQ\n"); zy7_ehci_detach(dev); return (ENOMEM); } /* Add USB device */ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); zy7_ehci_detach(dev); return (ENXIO); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, "Zynq-7000 ehci USB 2.0 controller"); strcpy(sc->sc_vendor, "Xilinx"); /* or IP vendor? */ /* Activate the interrupt */ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Cannot setup IRQ\n"); zy7_ehci_detach(dev); return (err); } /* Customization. */ sc->sc_flags |= EHCI_SCFLG_SETMODE | EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; /* Modify FIFO burst threshold from 2 to 8. */ bus_space_write_4(sc->sc_io_tag, bsh, ZY7_USB_TXFILLTUNING, 8 << ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT); /* Handle PHY options. */ if (zy7_phy_config(dev, sc->sc_io_tag, bsh) < 0) { device_printf(dev, "Cannot config phy!\n"); zy7_ehci_detach(dev); return (EIO); } /* Init ehci. */ err = ehci_init(sc); if (!err) { sc->sc_flags |= EHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(dev, "USB init failed err=%d\n", err); zy7_ehci_detach(dev); return (err); } return (0); } static int zy7_ehci_detach(device_t dev) { ehci_softc_t *sc = device_get_softc(dev); sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; if (device_is_attached(dev)) bus_generic_detach(dev); if (sc->sc_irq_res && sc->sc_intr_hdl) /* call ehci_detach() after ehci_init() called after * successful bus_setup_intr(). */ ehci_detach(sc); if (sc->sc_bus.bdev) { device_detach(sc->sc_bus.bdev); device_delete_child(dev, sc->sc_bus.bdev); } if (sc->sc_irq_res) { if (sc->sc_intr_hdl != NULL) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); } if (sc->sc_io_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_io_res), sc->sc_io_res); usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, zy7_ehci_probe), DEVMETHOD(device_attach, zy7_ehci_attach), DEVMETHOD(device_detach, zy7_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD_END }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(struct ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/at91dci_atmelarm.c =================================================================== --- head/sys/dev/usb/controller/at91dci_atmelarm.c (revision 276716) +++ head/sys/dev/usb/controller/at91dci_atmelarm.c (revision 276717) @@ -1,322 +1,323 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 /* Pin Definitions - do they belong here or somewhere else ? -- YES! */ #define VBUS_MASK AT91C_PIO_PB24 #define VBUS_BASE AT91RM92_PIOB_BASE #define PULLUP_MASK AT91C_PIO_PB22 #define PULLUP_BASE AT91RM92_PIOB_BASE static device_probe_t at91_udp_probe; static device_attach_t at91_udp_attach; static device_detach_t at91_udp_detach; struct at91_udp_softc { struct at91dci_softc sc_dci; /* must be first */ struct at91_pmc_clock *sc_mclk; struct at91_pmc_clock *sc_iclk; struct at91_pmc_clock *sc_fclk; struct callout sc_vbus; }; static void at91_vbus_poll(struct at91_udp_softc *sc) { uint8_t vbus_val; vbus_val = at91_pio_gpio_get(VBUS_BASE, VBUS_MASK) != 0; at91dci_vbus_interrupt(&sc->sc_dci, vbus_val); callout_reset(&sc->sc_vbus, hz, (void *)&at91_vbus_poll, sc); } static void at91_udp_clocks_on(void *arg) { struct at91_udp_softc *sc = arg; at91_pmc_clock_enable(sc->sc_mclk); at91_pmc_clock_enable(sc->sc_iclk); at91_pmc_clock_enable(sc->sc_fclk); } static void at91_udp_clocks_off(void *arg) { struct at91_udp_softc *sc = arg; at91_pmc_clock_disable(sc->sc_fclk); at91_pmc_clock_disable(sc->sc_iclk); at91_pmc_clock_disable(sc->sc_mclk); } static void at91_udp_pull_up(void *arg) { at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK); } static void at91_udp_pull_down(void *arg) { at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK); } static int at91_udp_probe(device_t dev) { device_set_desc(dev, "AT91 integrated AT91_UDP controller"); return (0); } static int at91_udp_attach(device_t dev) { struct at91_udp_softc *sc = device_get_softc(dev); int err; int rid; /* setup AT9100 USB device controller interface softc */ sc->sc_dci.sc_clocks_on = &at91_udp_clocks_on; sc->sc_dci.sc_clocks_off = &at91_udp_clocks_off; sc->sc_dci.sc_clocks_arg = sc; sc->sc_dci.sc_pull_up = &at91_udp_pull_up; sc->sc_dci.sc_pull_down = &at91_udp_pull_down; sc->sc_dci.sc_pull_arg = sc; /* initialise some bus fields */ sc->sc_dci.sc_bus.parent = dev; sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices; sc->sc_dci.sc_bus.devices_max = AT91_MAX_DEVICES; + sc->sc_dci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } callout_init_mtx(&sc->sc_vbus, &sc->sc_dci.sc_bus.bus_mtx, 0); /* * configure VBUS input pin, enable deglitch and enable * interrupt : */ at91_pio_use_gpio(VBUS_BASE, VBUS_MASK); at91_pio_gpio_input(VBUS_BASE, VBUS_MASK); at91_pio_gpio_set_deglitch(VBUS_BASE, VBUS_MASK, 1); at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 0); /* * configure PULLUP output pin : */ at91_pio_use_gpio(PULLUP_BASE, PULLUP_MASK); at91_pio_gpio_output(PULLUP_BASE, PULLUP_MASK, 0); at91_udp_pull_down(sc); /* wait 10ms for pulldown to stabilise */ usb_pause_mtx(NULL, hz / 100); sc->sc_mclk = at91_pmc_clock_ref("mck"); sc->sc_iclk = at91_pmc_clock_ref("udc_clk"); sc->sc_fclk = at91_pmc_clock_ref("udpck"); rid = MEM_RID; sc->sc_dci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_dci.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_dci.sc_io_tag = rman_get_bustag(sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_hdl = rman_get_bushandle(sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_size = rman_get_size(sc->sc_dci.sc_io_res); rid = 0; sc->sc_dci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_dci.sc_irq_res)) { goto error; } sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_dci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus); err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE, at91dci_filter_interrupt, at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl); if (err) { sc->sc_dci.sc_intr_hdl = NULL; goto error; } err = at91dci_init(&sc->sc_dci); if (!err) { err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev); } if (err) { goto error; } else { /* poll VBUS one time */ USB_BUS_LOCK(&sc->sc_dci.sc_bus); at91_vbus_poll(sc); USB_BUS_UNLOCK(&sc->sc_dci.sc_bus); } return (0); error: at91_udp_detach(dev); return (ENXIO); } static int at91_udp_detach(device_t dev) { struct at91_udp_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_dci.sc_bus.bdev) { bdev = sc->sc_dci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); USB_BUS_LOCK(&sc->sc_dci.sc_bus); callout_stop(&sc->sc_vbus); USB_BUS_UNLOCK(&sc->sc_dci.sc_bus); callout_drain(&sc->sc_vbus); /* disable Transceiver */ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS); /* disable and clear all interrupts */ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_IDR, 0xFFFFFFFF); AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_ICR, 0xFFFFFFFF); if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) { /* * only call at91_udp_uninit() after at91_udp_init() */ at91dci_uninit(&sc->sc_dci); err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res, sc->sc_dci.sc_intr_hdl); sc->sc_dci.sc_intr_hdl = NULL; } if (sc->sc_dci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_dci.sc_irq_res); sc->sc_dci.sc_irq_res = NULL; } if (sc->sc_dci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL); /* disable clocks */ at91_pmc_clock_disable(sc->sc_iclk); at91_pmc_clock_disable(sc->sc_fclk); at91_pmc_clock_disable(sc->sc_mclk); at91_pmc_clock_deref(sc->sc_fclk); at91_pmc_clock_deref(sc->sc_iclk); at91_pmc_clock_deref(sc->sc_mclk); return (0); } static device_method_t at91_udp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, at91_udp_probe), DEVMETHOD(device_attach, at91_udp_attach), DEVMETHOD(device_detach, at91_udp_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t at91_udp_driver = { .name = "at91_udp", .methods = at91_udp_methods, .size = sizeof(struct at91_udp_softc), }; static devclass_t at91_udp_devclass; DRIVER_MODULE(at91_udp, atmelarm, at91_udp_driver, at91_udp_devclass, 0, 0); Index: head/sys/dev/usb/controller/at91dci_fdt.c =================================================================== --- head/sys/dev/usb/controller/at91dci_fdt.c (revision 276716) +++ head/sys/dev/usb/controller/at91dci_fdt.c (revision 276717) @@ -1,328 +1,329 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 /* Pin Definitions - do they belong here or somewhere else ? -- YES! */ #define VBUS_MASK AT91C_PIO_PB24 #define VBUS_BASE AT91RM92_PIOB_BASE #define PULLUP_MASK AT91C_PIO_PB22 #define PULLUP_BASE AT91RM92_PIOB_BASE static device_probe_t at91_udp_probe; static device_attach_t at91_udp_attach; static device_detach_t at91_udp_detach; struct at91_udp_softc { struct at91dci_softc sc_dci; /* must be first */ struct at91_pmc_clock *sc_mclk; struct at91_pmc_clock *sc_iclk; struct at91_pmc_clock *sc_fclk; struct callout sc_vbus; }; static void at91_vbus_poll(struct at91_udp_softc *sc) { uint8_t vbus_val; vbus_val = at91_pio_gpio_get(VBUS_BASE, VBUS_MASK) != 0; at91dci_vbus_interrupt(&sc->sc_dci, vbus_val); callout_reset(&sc->sc_vbus, hz, (void *)&at91_vbus_poll, sc); } static void at91_udp_clocks_on(void *arg) { struct at91_udp_softc *sc = arg; at91_pmc_clock_enable(sc->sc_mclk); at91_pmc_clock_enable(sc->sc_iclk); at91_pmc_clock_enable(sc->sc_fclk); } static void at91_udp_clocks_off(void *arg) { struct at91_udp_softc *sc = arg; at91_pmc_clock_disable(sc->sc_fclk); at91_pmc_clock_disable(sc->sc_iclk); at91_pmc_clock_disable(sc->sc_mclk); } static void at91_udp_pull_up(void *arg) { at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK); } static void at91_udp_pull_down(void *arg) { at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK); } static int at91_udp_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "atmel,at91rm9200-udc")) return (ENXIO); device_set_desc(dev, "AT91 integrated AT91_UDP controller"); return (0); } static int at91_udp_attach(device_t dev) { struct at91_udp_softc *sc = device_get_softc(dev); int err; int rid; /* setup AT9100 USB device controller interface softc */ sc->sc_dci.sc_clocks_on = &at91_udp_clocks_on; sc->sc_dci.sc_clocks_off = &at91_udp_clocks_off; sc->sc_dci.sc_clocks_arg = sc; sc->sc_dci.sc_pull_up = &at91_udp_pull_up; sc->sc_dci.sc_pull_down = &at91_udp_pull_down; sc->sc_dci.sc_pull_arg = sc; /* initialise some bus fields */ sc->sc_dci.sc_bus.parent = dev; sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices; sc->sc_dci.sc_bus.devices_max = AT91_MAX_DEVICES; + sc->sc_dci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } callout_init_mtx(&sc->sc_vbus, &sc->sc_dci.sc_bus.bus_mtx, 0); /* * configure VBUS input pin, enable deglitch and enable * interrupt : */ at91_pio_use_gpio(VBUS_BASE, VBUS_MASK); at91_pio_gpio_input(VBUS_BASE, VBUS_MASK); at91_pio_gpio_set_deglitch(VBUS_BASE, VBUS_MASK, 1); at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 0); /* * configure PULLUP output pin : */ at91_pio_use_gpio(PULLUP_BASE, PULLUP_MASK); at91_pio_gpio_output(PULLUP_BASE, PULLUP_MASK, 0); at91_udp_pull_down(sc); /* wait 10ms for pulldown to stabilise */ usb_pause_mtx(NULL, hz / 100); sc->sc_mclk = at91_pmc_clock_ref("mck"); sc->sc_iclk = at91_pmc_clock_ref("udc_clk"); sc->sc_fclk = at91_pmc_clock_ref("udpck"); rid = MEM_RID; sc->sc_dci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_dci.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_dci.sc_io_tag = rman_get_bustag(sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_hdl = rman_get_bushandle(sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_size = rman_get_size(sc->sc_dci.sc_io_res); rid = 0; sc->sc_dci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_dci.sc_irq_res)) { goto error; } sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_dci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus); err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE, at91dci_filter_interrupt, at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl); if (err) { sc->sc_dci.sc_intr_hdl = NULL; goto error; } err = at91dci_init(&sc->sc_dci); if (!err) { err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev); } if (err) { goto error; } else { /* poll VBUS one time */ USB_BUS_LOCK(&sc->sc_dci.sc_bus); at91_vbus_poll(sc); USB_BUS_UNLOCK(&sc->sc_dci.sc_bus); } return (0); error: at91_udp_detach(dev); return (ENXIO); } static int at91_udp_detach(device_t dev) { struct at91_udp_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_dci.sc_bus.bdev) { bdev = sc->sc_dci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); USB_BUS_LOCK(&sc->sc_dci.sc_bus); callout_stop(&sc->sc_vbus); USB_BUS_UNLOCK(&sc->sc_dci.sc_bus); callout_drain(&sc->sc_vbus); /* disable Transceiver */ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS); /* disable and clear all interrupts */ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_IDR, 0xFFFFFFFF); AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_ICR, 0xFFFFFFFF); if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) { /* * only call at91_udp_uninit() after at91_udp_init() */ at91dci_uninit(&sc->sc_dci); err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res, sc->sc_dci.sc_intr_hdl); sc->sc_dci.sc_intr_hdl = NULL; } if (sc->sc_dci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_dci.sc_irq_res); sc->sc_dci.sc_irq_res = NULL; } if (sc->sc_dci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL); /* disable clocks */ at91_pmc_clock_disable(sc->sc_iclk); at91_pmc_clock_disable(sc->sc_fclk); at91_pmc_clock_disable(sc->sc_mclk); at91_pmc_clock_deref(sc->sc_fclk); at91_pmc_clock_deref(sc->sc_iclk); at91_pmc_clock_deref(sc->sc_mclk); return (0); } static device_method_t at91_udp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, at91_udp_probe), DEVMETHOD(device_attach, at91_udp_attach), DEVMETHOD(device_detach, at91_udp_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t at91_udp_driver = { .name = "at91_udp", .methods = at91_udp_methods, .size = sizeof(struct at91_udp_softc), }; static devclass_t at91_udp_devclass; DRIVER_MODULE(at91_udp, simplebus, at91_udp_driver, at91_udp_devclass, 0, 0); Index: head/sys/dev/usb/controller/atmegadci_atmelarm.c =================================================================== --- head/sys/dev/usb/controller/atmegadci_atmelarm.c (revision 276716) +++ head/sys/dev/usb/controller/atmegadci_atmelarm.c (revision 276717) @@ -1,216 +1,217 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2009 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t atmegadci_probe; static device_attach_t atmegadci_attach; static device_detach_t atmegadci_detach; struct atmegadci_super_softc { struct atmegadci_softc sc_otg; /* must be first */ }; static void atmegadci_clocks_on(struct usb_bus *bus) { /* TODO */ } static void atmegadci_clocks_off(struct usb_bus *bus) { /* TODO */ } static int atmegadci_probe(device_t dev) { device_set_desc(dev, "ATMEL OTG integrated USB controller"); return (0); } static int atmegadci_attach(device_t dev) { struct atmegadci_super_softc *sc = device_get_softc(dev); int err; int rid; /* setup MUSB OTG USB controller interface softc */ sc->sc_otg.sc_clocks_on = &atmegadci_clocks_on; sc->sc_otg.sc_clocks_off = &atmegadci_clocks_off; /* initialise some bus fields */ sc->sc_otg.sc_bus.parent = dev; sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; sc->sc_otg.sc_bus.devices_max = ATMEGA_MAX_DEVICES; + sc->sc_otg.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_otg.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); rid = 0; sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_irq_res)) { goto error; } sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_otg.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)atmegadci_interrupt, sc, &sc->sc_otg.sc_intr_hdl); if (err) { sc->sc_otg.sc_intr_hdl = NULL; goto error; } err = atmegadci_init(&sc->sc_otg); if (!err) { err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); } if (err) { goto error; } return (0); error: atmegadci_detach(dev); return (ENXIO); } static int atmegadci_detach(device_t dev) { struct atmegadci_super_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_otg.sc_bus.bdev) { bdev = sc->sc_otg.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { /* * only call atmegadci_uninit() after atmegadci_init() */ atmegadci_uninit(&sc->sc_otg); err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, sc->sc_otg.sc_intr_hdl); sc->sc_otg.sc_intr_hdl = NULL; } /* free IRQ channel, if any */ if (sc->sc_otg.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_otg.sc_irq_res); sc->sc_otg.sc_irq_res = NULL; } /* free memory resource, if any */ if (sc->sc_otg.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); return (0); } static device_method_t atmegadci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, atmegadci_probe), DEVMETHOD(device_attach, atmegadci_attach), DEVMETHOD(device_detach, atmegadci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t atmegadci_driver = { .name = "atmegadci", .methods = atmegadci_methods, .size = sizeof(struct atmegadci_super_softc), }; static devclass_t atmegadci_devclass; DRIVER_MODULE(atmegadci, atmelarm, atmegadci_driver, atmegadci_devclass, 0, 0); MODULE_DEPEND(atmegadci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/dwc_otg_fdt.c =================================================================== --- head/sys/dev/usb/controller/dwc_otg_fdt.c (revision 276716) +++ head/sys/dev/usb/controller/dwc_otg_fdt.c (revision 276717) @@ -1,233 +1,234 @@ /*- * Copyright (c) 2012 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t dwc_otg_probe; static device_attach_t dwc_otg_attach; static device_detach_t dwc_otg_detach; struct dwc_otg_super_softc { struct dwc_otg_softc sc_otg; /* must be first */ }; static int dwc_otg_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "synopsys,designware-hs-otg2")) return (ENXIO); device_set_desc(dev, "DWC OTG 2.0 integrated USB controller"); return (0); } static int dwc_otg_attach(device_t dev) { struct dwc_otg_super_softc *sc = device_get_softc(dev); char usb_mode[24]; int err; int rid; /* initialise some bus fields */ sc->sc_otg.sc_bus.parent = dev; sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; sc->sc_otg.sc_bus.devices_max = DWC_OTG_MAX_DEVICES; + sc->sc_otg.sc_bus.dma_bits = 32; /* get USB mode, if any */ if (OF_getprop(ofw_bus_get_node(dev), "dr_mode", &usb_mode, sizeof(usb_mode)) > 0) { /* ensure proper zero termination */ usb_mode[sizeof(usb_mode) - 1] = 0; if (strcasecmp(usb_mode, "host") == 0) sc->sc_otg.sc_mode = DWC_MODE_HOST; else if (strcasecmp(usb_mode, "peripheral") == 0) sc->sc_otg.sc_mode = DWC_MODE_DEVICE; else if (strcasecmp(usb_mode, "otg") != 0) { device_printf(dev, "Invalid FDT dr_mode: %s\n", usb_mode); } } /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_otg.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res); rid = 0; sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_otg.sc_irq_res == NULL) goto error; sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->sc_otg.sc_bus.bdev == NULL) goto error; device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE, &dwc_otg_filter_interrupt, &dwc_otg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); if (err) { sc->sc_otg.sc_intr_hdl = NULL; goto error; } err = dwc_otg_init(&sc->sc_otg); if (err == 0) { err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); } if (err) goto error; return (0); error: dwc_otg_detach(dev); return (ENXIO); } static int dwc_otg_detach(device_t dev) { struct dwc_otg_super_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_otg.sc_bus.bdev) { bdev = sc->sc_otg.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { /* * only call dwc_otg_uninit() after dwc_otg_init() */ dwc_otg_uninit(&sc->sc_otg); err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, sc->sc_otg.sc_intr_hdl); sc->sc_otg.sc_intr_hdl = NULL; } /* free IRQ channel, if any */ if (sc->sc_otg.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_otg.sc_irq_res); sc->sc_otg.sc_irq_res = NULL; } /* free memory resource, if any */ if (sc->sc_otg.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); return (0); } static device_method_t dwc_otg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dwc_otg_probe), DEVMETHOD(device_attach, dwc_otg_attach), DEVMETHOD(device_detach, dwc_otg_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t dwc_otg_driver = { .name = "dwcotg", .methods = dwc_otg_methods, .size = sizeof(struct dwc_otg_super_softc), }; static devclass_t dwc_otg_devclass; DRIVER_MODULE(dwcotg, simplebus, dwc_otg_driver, dwc_otg_devclass, 0, 0); MODULE_DEPEND(dwcotg, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ehci_fsl.c =================================================================== --- head/sys/dev/usb/controller/ehci_fsl.c (revision 276716) +++ head/sys/dev/usb/controller/ehci_fsl.c (revision 276717) @@ -1,426 +1,427 @@ /*- * Copyright (c) 2010-2012 Semihalf * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_platform.h" /* * Register the driver */ /* Forward declarations */ static int fsl_ehci_attach(device_t self); static int fsl_ehci_detach(device_t self); static int fsl_ehci_probe(device_t self); static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fsl_ehci_probe), DEVMETHOD(device_attach, fsl_ehci_attach), DEVMETHOD(device_detach, fsl_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; /* kobj_class definition */ static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(struct ehci_softc) }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); /* * Private defines */ #define FSL_EHCI_REG_OFF 0x100 #define FSL_EHCI_REG_SIZE 0x300 /* * Internal interface registers' offsets. * Offsets from 0x000 ehci dev space, big-endian access. */ enum internal_reg { SNOOP1 = 0x400, SNOOP2 = 0x404, AGE_CNT_THRESH = 0x408, SI_CTRL = 0x410, CONTROL = 0x500 }; /* CONTROL register bit flags */ enum control_flags { USB_EN = 0x00000004, UTMI_PHY_EN = 0x00000200, ULPI_INT_EN = 0x00000001 }; /* SI_CTRL register bit flags */ enum si_ctrl_flags { FETCH_32 = 1, FETCH_64 = 0 }; #define SNOOP_RANGE_2GB 0x1E /* * Operational registers' offsets. * Offsets from USBCMD register, little-endian access. */ enum special_op_reg { USBMODE = 0x0A8, PORTSC = 0x084, ULPI_VIEWPORT = 0x70 }; /* USBMODE register bit flags */ enum usbmode_flags { HOST_MODE = 0x3, DEVICE_MODE = 0x2 }; #define PORT_POWER_MASK 0x00001000 /* * Private methods */ static void set_to_host_mode(ehci_softc_t *sc) { int tmp; tmp = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, USBMODE); bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, USBMODE, tmp | HOST_MODE); } static void enable_usb(device_t dev, bus_space_tag_t iot, bus_space_handle_t ioh) { int tmp; phandle_t node; char *phy_type; phy_type = NULL; tmp = bus_space_read_4(iot, ioh, CONTROL) | USB_EN; node = ofw_bus_get_node(dev); if ((node != 0) && (OF_getprop_alloc(node, "phy_type", 1, (void **)&phy_type) > 0)) { if (strncasecmp(phy_type, "utmi", strlen("utmi")) == 0) tmp |= UTMI_PHY_EN; free(phy_type, M_OFWPROP); } bus_space_write_4(iot, ioh, CONTROL, tmp); } static void set_32b_prefetch(bus_space_tag_t iot, bus_space_handle_t ioh) { bus_space_write_4(iot, ioh, SI_CTRL, FETCH_32); } static void set_snooping(bus_space_tag_t iot, bus_space_handle_t ioh) { bus_space_write_4(iot, ioh, SNOOP1, SNOOP_RANGE_2GB); bus_space_write_4(iot, ioh, SNOOP2, 0x80000000 | SNOOP_RANGE_2GB); } static void clear_port_power(ehci_softc_t *sc) { int tmp; tmp = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, PORTSC); bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, PORTSC, tmp & ~PORT_POWER_MASK); } /* * Public methods */ static int fsl_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (((ofw_bus_is_compatible(dev, "fsl-usb2-dr")) == 0) && ((ofw_bus_is_compatible(dev, "fsl-usb2-mph")) == 0)) return (ENXIO); device_set_desc(dev, "Freescale integrated EHCI controller"); return (BUS_PROBE_DEFAULT); } static int fsl_ehci_attach(device_t self) { ehci_softc_t *sc; int rid; int err; bus_space_handle_t ioh; bus_space_tag_t iot; sc = device_get_softc(self); rid = 0; sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) return (ENOMEM); /* Allocate io resource for EHCI */ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_io_res == NULL) { err = fsl_ehci_detach(self); if (err) { device_printf(self, "Detach of the driver failed with error %d\n", err); } return (ENXIO); } iot = rman_get_bustag(sc->sc_io_res); /* * Set handle to USB related registers subregion used by generic * EHCI driver */ ioh = rman_get_bushandle(sc->sc_io_res); err = bus_space_subregion(iot, ioh, FSL_EHCI_REG_OFF, FSL_EHCI_REG_SIZE, &sc->sc_io_hdl); if (err != 0) { err = fsl_ehci_detach(self); if (err) { device_printf(self, "Detach of the driver failed with error %d\n", err); } return (ENXIO); } /* Set little-endian tag for use by the generic EHCI driver */ sc->sc_io_tag = &bs_le_tag; /* Allocate irq */ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { err = fsl_ehci_detach(self); if (err) { device_printf(self, "Detach of the driver failed with error %d\n", err); } return (ENXIO); } /* Setup interrupt handler */ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; err = fsl_ehci_detach(self); if (err) { device_printf(self, "Detach of the driver failed with error %d\n", err); } return (ENXIO); } /* Add USB device */ sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); err = fsl_ehci_detach(self); if (err) { device_printf(self, "Detach of the driver failed with error %d\n", err); } return (ENOMEM); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); sc->sc_id_vendor = 0x1234; strlcpy(sc->sc_vendor, "Freescale", sizeof(sc->sc_vendor)); /* Enable USB */ err = ehci_reset(sc); if (err) { device_printf(self, "Could not reset the controller\n"); err = fsl_ehci_detach(self); if (err) { device_printf(self, "Detach of the driver failed with error %d\n", err); } return (ENXIO); } enable_usb(self, iot, ioh); set_snooping(iot, ioh); set_to_host_mode(sc); set_32b_prefetch(iot, ioh); /* * If usb subsystem is enabled in U-Boot, port power has to be turned * off to allow proper discovery of devices during boot up. */ clear_port_power(sc); /* Set flags */ sc->sc_flags |= EHCI_SCFLG_DONTRESET | EHCI_SCFLG_NORESTERM; err = ehci_init(sc); if (!err) { sc->sc_flags |= EHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); err = fsl_ehci_detach(self); if (err) { device_printf(self, "Detach of the driver failed with error %d\n", err); } return (EIO); } return (0); } static int fsl_ehci_detach(device_t self) { int err; ehci_softc_t *sc; sc = device_get_softc(self); /* * only call ehci_detach() after ehci_init() */ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { ehci_detach(sc); sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; } /* Disable interrupts that might have been switched on in ehci_init */ if (sc->sc_io_tag && sc->sc_io_hdl) bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, EHCI_USBINTR, 0); if (sc->sc_irq_res && sc->sc_intr_hdl) { err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) { device_printf(self, "Could not tear down irq, %d\n", err); return (err); } sc->sc_intr_hdl = NULL; } if (sc->sc_bus.bdev) { device_delete_child(self, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; } /* During module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; sc->sc_io_tag = 0; sc->sc_io_hdl = 0; } return (0); } Index: head/sys/dev/usb/controller/ehci_imx.c =================================================================== --- head/sys/dev/usb/controller/ehci_imx.c (revision 276716) +++ head/sys/dev/usb/controller/ehci_imx.c (revision 276717) @@ -1,335 +1,337 @@ /*- * Copyright (c) 2010-2012 Semihalf * Copyright (c) 2012 The FreeBSD Foundation * Copyright (c) 2013 Ian Lepore * All rights reserved. * * Portions of this software were developed by Oleksandr Rybalko * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * EHCI driver for Freescale i.MX SoCs which incorporate the USBOH3 controller. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include "opt_platform.h" /* * Notes on the hardware and related FDT data seen in the wild. * * There are two sets of registers in the USBOH3 implementation; documentation * refers to them as "core" and "non-core" registers. A set of core register * exists for each OTG or EHCI device. There is a single set of non-core * registers per USBOH3, and they control aspects of operation not directly * related to the USB specs, such as whether interrupts from each of the core * devices are able to generate a SoC wakeup event. * * In the FreeBSD universe we might be inclined to describe the core and * non-core registers by using a pair of resource address/size values (two * entries in the reg property for each core). However, we have to work with * existing FDT data (which mostly comes from the linux universe), and the way * they've chosen to represent this is with an entry for a "usbmisc" device * whose reg property describes the non-core registers. The way we handle FDT * data, this means that the resources (memory-mapped register range) for the * non-core registers belongs to a device other than the echi devices. * * At the moment we have no need to access the non-core registers, so all of * this amounts to documenting what's known. The following compat strings have * been seen in existing FDT data: * - "fsl,imx25-usbmisc" * - "fsl,imx51-usbmisc"; * - "fsl,imx6q-usbmisc"; * * In addition to the single usbmisc device, the existing FDT data defines a * separate device for each of the OTG or EHCI cores within the USBOH3. Each of * those devices has a set of core registers described by the reg property. * * The core registers for each of the four cores in the USBOH3 are divided into * two parts: a set of imx-specific registers at an offset of 0 from the * beginning of the register range, and the standard USB (EHCI or OTG) registers * at an offset of 0x100 from the beginning of the register range. The FreeBSD * way of dealing with this might be to map out two ranges in the reg property, * but that's not what the alternate universe has done. To work with existing * FDT data, we acquire the resource that maps all the core registers, then use * bus_space_subregion() to create another resource that maps just the standard * USB registers, which we provide to the standard USB code in the ehci_softc. * * The following compat strings have been seen for the OTG and EHCI cores. The * FDT compat table in this driver contains all these strings, but as of this * writing, not all of these SoCs have been tested with the driver. The fact * that imx27 is common to all of them gives some hope that the driver will work * on all these SoCs. * - "fsl,imx23-usb", "fsl,imx27-usb"; * - "fsl,imx25-usb", "fsl,imx27-usb"; * - "fsl,imx28-usb", "fsl,imx27-usb"; * - "fsl,imx51-usb", "fsl,imx27-usb"; * - "fsl,imx53-usb", "fsl,imx27-usb"; * - "fsl,imx6q-usb", "fsl,imx27-usb"; * * The FDT data for some SoCs contains the following properties, which we don't * currently do anything with: * - fsl,usbmisc = <&usbmisc 0>; * - fsl,usbphy = <&usbphy0>; * * Some imx SoCs have FDT data related to USB PHY, some don't. We have separate * usbphy drivers where needed; this data is mentioned here just to keep all the * imx-FDT-usb-related info in one place. Here are the usbphy compat strings * known to exist: * - "nop-usbphy" * - "usb-nop-xceiv"; * - "fsl,imx23-usbphy" * - "fsl,imx28-usbphy", "fsl,imx23-usbphy"; * - "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; * */ static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-usb", 1}, {"fsl,imx53-usb", 1}, {"fsl,imx51-usb", 1}, {"fsl,imx28-usb", 1}, {"fsl,imx27-usb", 1}, {"fsl,imx25-usb", 1}, {"fsl,imx23-usb", 1}, {NULL, 0}, }; /* * Each EHCI device in the SoC has some SoC-specific per-device registers at an * offset of 0, then the standard EHCI registers begin at an offset of 0x100. */ #define IMX_EHCI_REG_OFF 0x100 #define IMX_EHCI_REG_SIZE 0x100 struct imx_ehci_softc { ehci_softc_t ehci_softc; struct resource *ehci_mem_res; /* EHCI core regs. */ struct resource *ehci_irq_res; /* EHCI core IRQ. */ }; static int imx_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Freescale i.MX integrated USB controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int imx_ehci_detach(device_t dev) { struct imx_ehci_softc *sc; ehci_softc_t *esc; sc = device_get_softc(dev); esc = &sc->ehci_softc; if (esc->sc_bus.bdev != NULL) device_delete_child(dev, esc->sc_bus.bdev); if (esc->sc_flags & EHCI_SCFLG_DONEINIT) ehci_detach(esc); if (esc->sc_intr_hdl != NULL) bus_teardown_intr(dev, esc->sc_irq_res, esc->sc_intr_hdl); if (sc->ehci_irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ehci_irq_res); if (sc->ehci_mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->ehci_mem_res); usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc); /* During module unload there are lots of children leftover */ device_delete_children(dev); return (0); } static int imx_ehci_attach(device_t dev) { struct imx_ehci_softc *sc; ehci_softc_t *esc; int err, rid; sc = device_get_softc(dev); esc = &sc->ehci_softc; err = 0; /* Allocate bus_space resources. */ rid = 0; sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->ehci_mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); err = ENXIO; goto out; } rid = 0; sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->ehci_irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); err = ENXIO; goto out; } esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res); esc->sc_bus.parent = dev; esc->sc_bus.devices = esc->sc_devices; esc->sc_bus.devices_max = EHCI_MAX_DEVICES; + esc->sc_bus.dma_bits = 32; + /* allocate all DMA memory */ if (usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc) != 0) { device_printf(dev, "usb_bus_mem_alloc_all() failed\n"); err = ENOMEM; goto out; } /* * Set handle to USB related registers subregion used by * generic EHCI driver. */ err = bus_space_subregion(esc->sc_io_tag, rman_get_bushandle(sc->ehci_mem_res), IMX_EHCI_REG_OFF, IMX_EHCI_REG_SIZE, &esc->sc_io_hdl); if (err != 0) { device_printf(dev, "bus_space_subregion() failed\n"); err = ENXIO; goto out; } /* Setup interrupt handler. */ err = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO, NULL, (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); if (err != 0) { device_printf(dev, "Could not setup IRQ\n"); goto out; } /* Turn on clocks. */ imx_ccm_usb_enable(dev); /* Add USB bus device. */ esc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (esc->sc_bus.bdev == NULL) { device_printf(dev, "Could not add USB device\n"); goto out; } device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus); esc->sc_id_vendor = USB_VENDOR_FREESCALE; strlcpy(esc->sc_vendor, "Freescale", sizeof(esc->sc_vendor)); /* Set flags that affect ehci_init() behavior. */ esc->sc_flags |= EHCI_SCFLG_DONTRESET | EHCI_SCFLG_NORESTERM; err = ehci_init(esc); if (err != 0) { device_printf(dev, "USB init failed, usb_err_t=%d\n", err); goto out; } esc->sc_flags |= EHCI_SCFLG_DONEINIT; /* Probe the bus. */ err = device_probe_and_attach(esc->sc_bus.bdev); if (err != 0) { device_printf(dev, "device_probe_and_attach() failed\n"); goto out; } err = 0; out: if (err != 0) imx_ehci_detach(dev); return (err); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, imx_ehci_probe), DEVMETHOD(device_attach, imx_ehci_attach), DEVMETHOD(device_detach, imx_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD_END }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(struct imx_ehci_softc) }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ehci_ixp4xx.c =================================================================== --- head/sys/dev/usb/controller/ehci_ixp4xx.c (revision 276716) +++ head/sys/dev/usb/controller/ehci_ixp4xx.c (revision 276717) @@ -1,312 +1,313 @@ /*- * Copyright (c) 2008 Sam Leffler. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * IXP435 attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EHCI_VENDORID_IXP4XX 0x42fa05 #define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller" struct ixp_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ bus_space_tag_t iot; bus_space_handle_t ioh; struct bus_space tag; /* tag for private bus space ops */ }; static device_attach_t ehci_ixp_attach; static device_detach_t ehci_ixp_detach; static uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t); static void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t); static uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t); static void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t); static uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t); static void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t); static int ehci_ixp_probe(device_t self) { device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } static int ehci_ixp_attach(device_t self) { struct ixp_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } /* NB: hints fix the memory location and irq */ rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } /* * Craft special resource for bus space ops that handle * byte-alignment of non-word addresses. Also, since * we're already intercepting bus space ops we handle * the register window offset that could otherwise be * done with bus_space_subregion. */ isc->iot = rman_get_bustag(sc->sc_io_res); isc->tag.bs_cookie = isc->iot; /* read single */ isc->tag.bs_r_1 = ehci_bs_r_1, isc->tag.bs_r_2 = ehci_bs_r_2, isc->tag.bs_r_4 = ehci_bs_r_4, /* write (single) */ isc->tag.bs_w_1 = ehci_bs_w_1, isc->tag.bs_w_2 = ehci_bs_w_2, isc->tag.bs_w_4 = ehci_bs_w_4, sc->sc_io_tag = &isc->tag; sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = IXP435_USB1_SIZE - 0x100; rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Intel"); err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* * Arrange to force Host mode, select big-endian byte alignment, * and arrange to not terminate reset operations (the adapter * will ignore it if we do but might as well save a reg write). * Also, the controller has an embedded Transaction Translator * which means port speed must be read from the Port Status * register following a port enable. */ sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_SETMODE | EHCI_SCFLG_BIGEDESC | EHCI_SCFLG_BIGEMMIO | EHCI_SCFLG_NORESTERM ; err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: ehci_ixp_detach(self); return (ENXIO); } static int ehci_ixp_detach(device_t self) { struct ixp_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } /* * Bus space accessors for PIO operations. */ static uint8_t ehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o) { return bus_space_read_1((bus_space_tag_t) t, h, 0x100 + (o &~ 3) + (3 - (o & 3))); } static void ehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v) { panic("%s", __func__); } static uint16_t ehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o) { return bus_space_read_2((bus_space_tag_t) t, h, 0x100 + (o &~ 3) + (2 - (o & 3))); } static void ehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v) { panic("%s", __func__); } static uint32_t ehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o) { return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o); } static void ehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v) { bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ehci_ixp_probe), DEVMETHOD(device_attach, ehci_ixp_attach), DEVMETHOD(device_detach, ehci_ixp_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(struct ixp_ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ehci_mv.c =================================================================== --- head/sys/dev/usb/controller/ehci_mv.c (revision 276716) +++ head/sys/dev/usb/controller/ehci_mv.c (revision 276717) @@ -1,352 +1,353 @@ /*- * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. * All rights reserved. * * Developed by Semihalf. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of MARVELL nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * FDT attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EHCI_VENDORID_MRVL 0x1286 #define EHCI_HC_DEVSTR "Marvell Integrated USB 2.0 controller" static device_attach_t mv_ehci_attach; static device_detach_t mv_ehci_detach; static int err_intr(void *arg); static struct resource *irq_err; static void *ih_err; /* EHCI HC regs start at this offset within USB range */ #define MV_USB_HOST_OFST 0x0100 #define USB_BRIDGE_INTR_CAUSE 0x210 #define USB_BRIDGE_INTR_MASK 0x214 #define USB_BRIDGE_ERR_ADDR 0x21C #define MV_USB_ADDR_DECODE_ERR (1 << 0) #define MV_USB_HOST_UNDERFLOW (1 << 1) #define MV_USB_HOST_OVERFLOW (1 << 2) #define MV_USB_DEVICE_UNDERFLOW (1 << 3) static int mv_ehci_probe(device_t self) { if (!ofw_bus_status_okay(self)) return (ENXIO); if (!ofw_bus_is_compatible(self, "mrvl,usb-ehci")) return (ENXIO); device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } static int mv_ehci_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); bus_space_handle_t bsh; int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); bsh = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res) - MV_USB_HOST_OFST; /* * Marvell EHCI host controller registers start at certain offset * within the whole USB registers range, so create a subregion for the * host mode configuration purposes. */ if (bus_space_subregion(sc->sc_io_tag, bsh, MV_USB_HOST_OFST, sc->sc_io_size, &sc->sc_io_hdl) != 0) panic("%s: unable to subregion USB host registers", device_get_name(self)); rid = 0; irq_err = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (irq_err == NULL) { device_printf(self, "Could not allocate error irq\n"); mv_ehci_detach(self); return (ENXIO); } /* * Notice: Marvell EHCI controller has TWO interrupt lines, so make * sure to use the correct rid for the main one (controller interrupt) * -- refer to DTS for the right resource number to use here. */ rid = 1; 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Marvell"); err = bus_setup_intr(self, irq_err, INTR_TYPE_BIO, err_intr, NULL, sc, &ih_err); if (err) { device_printf(self, "Could not setup error irq, %d\n", err); ih_err = NULL; goto error; } EWRITE4(sc, USB_BRIDGE_INTR_MASK, MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW); err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* * Workaround for Marvell integrated EHCI controller: reset of * the EHCI core clears the USBMODE register, which sets the core in * an undefined state (neither host nor agent), so it needs to be set * again for proper operation. * * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for * details. */ sc->sc_flags |= EHCI_SCFLG_SETMODE; if (bootverbose) device_printf(self, "5.24 GL USB-2 workaround enabled\n"); /* XXX all MV chips need it? */ sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM; err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: mv_ehci_detach(self); return (ENXIO); } static int mv_ehci_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); /* * disable interrupts that might have been switched on in mv_ehci_attach */ if (sc->sc_io_res) { EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); } if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (irq_err && ih_err) { err = bus_teardown_intr(self, irq_err, ih_err); if (err) device_printf(self, "Could not tear down irq, %d\n", err); ih_err = NULL; } if (irq_err) { bus_release_resource(self, SYS_RES_IRQ, 0, irq_err); irq_err = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static int err_intr(void *arg) { ehci_softc_t *sc = arg; unsigned int cause; cause = EREAD4(sc, USB_BRIDGE_INTR_CAUSE); if (cause) { printf("USB error: "); if (cause & MV_USB_ADDR_DECODE_ERR) { uint32_t addr; addr = EREAD4(sc, USB_BRIDGE_ERR_ADDR); printf("address decoding error (addr=%#x)\n", addr); } if (cause & MV_USB_HOST_UNDERFLOW) printf("host underflow\n"); if (cause & MV_USB_HOST_OVERFLOW) printf("host overflow\n"); if (cause & MV_USB_DEVICE_UNDERFLOW) printf("device underflow\n"); if (cause & ~(MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW)) printf("unknown cause (cause=%#x)\n", cause); EWRITE4(sc, USB_BRIDGE_INTR_CAUSE, 0); } return (FILTER_HANDLED); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mv_ehci_probe), DEVMETHOD(device_attach, mv_ehci_attach), DEVMETHOD(device_detach, mv_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(ehci_softc_t), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ehci_pci.c =================================================================== --- head/sys/dev/usb/controller/ehci_pci.c (revision 276716) +++ head/sys/dev/usb/controller/ehci_pci.c (revision 276717) @@ -1,581 +1,582 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. * * The EHCI 1.0 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r10.pdf * and the USB 2.0 spec at * http://www.usb.org/developers/docs/usb_20.zip */ /* The low level controller code for EHCI has been split into * PCI probes and EHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usb_if.h" #define PCI_EHCI_VENDORID_ACERLABS 0x10b9 #define PCI_EHCI_VENDORID_AMD 0x1022 #define PCI_EHCI_VENDORID_APPLE 0x106b #define PCI_EHCI_VENDORID_ATI 0x1002 #define PCI_EHCI_VENDORID_CMDTECH 0x1095 #define PCI_EHCI_VENDORID_INTEL 0x8086 #define PCI_EHCI_VENDORID_NEC 0x1033 #define PCI_EHCI_VENDORID_OPTI 0x1045 #define PCI_EHCI_VENDORID_PHILIPS 0x1131 #define PCI_EHCI_VENDORID_SIS 0x1039 #define PCI_EHCI_VENDORID_NVIDIA 0x12D2 #define PCI_EHCI_VENDORID_NVIDIA2 0x10DE #define PCI_EHCI_VENDORID_VIA 0x1106 static device_probe_t ehci_pci_probe; static device_attach_t ehci_pci_attach; static device_detach_t ehci_pci_detach; static usb_take_controller_t ehci_pci_take_controller; static const char * ehci_pci_match(device_t self) { uint32_t device_id = pci_get_devid(self); switch (device_id) { case 0x523910b9: return "ALi M5239 USB 2.0 controller"; case 0x10227463: return "AMD 8111 USB 2.0 controller"; case 0x20951022: return ("AMD CS5536 (Geode) USB 2.0 controller"); case 0x43451002: return "ATI SB200 USB 2.0 controller"; case 0x43731002: return "ATI SB400 USB 2.0 controller"; case 0x43961002: return ("AMD SB7x0/SB8x0/SB9x0 USB 2.0 controller"); case 0x0f348086: return ("Intel BayTrail USB 2.0 controller"); case 0x1d268086: return ("Intel Patsburg USB 2.0 controller"); case 0x1d2d8086: return ("Intel Patsburg USB 2.0 controller"); case 0x1e268086: return ("Intel Panther Point USB 2.0 controller"); case 0x1e2d8086: return ("Intel Panther Point USB 2.0 controller"); case 0x1f2c8086: return ("Intel Avoton USB 2.0 controller"); case 0x25ad8086: return "Intel 6300ESB USB 2.0 controller"; case 0x24cd8086: return "Intel 82801DB/L/M (ICH4) USB 2.0 controller"; case 0x24dd8086: return "Intel 82801EB/R (ICH5) USB 2.0 controller"; case 0x265c8086: return "Intel 82801FB (ICH6) USB 2.0 controller"; case 0x268c8086: return ("Intel 63XXESB USB 2.0 controller"); case 0x27cc8086: return "Intel 82801GB/R (ICH7) USB 2.0 controller"; case 0x28368086: return "Intel 82801H (ICH8) USB 2.0 controller USB2-A"; case 0x283a8086: return "Intel 82801H (ICH8) USB 2.0 controller USB2-B"; case 0x293a8086: return "Intel 82801I (ICH9) USB 2.0 controller"; case 0x293c8086: return "Intel 82801I (ICH9) USB 2.0 controller"; case 0x3a3a8086: return "Intel 82801JI (ICH10) USB 2.0 controller USB-A"; case 0x3a3c8086: return "Intel 82801JI (ICH10) USB 2.0 controller USB-B"; case 0x3b348086: return ("Intel PCH USB 2.0 controller USB-A"); case 0x3b3c8086: return ("Intel PCH USB 2.0 controller USB-B"); case 0x8c268086: return ("Intel Lynx Point USB 2.0 controller USB-A"); case 0x8c2d8086: return ("Intel Lynx Point USB 2.0 controller USB-B"); case 0x8ca68086: return ("Intel Wildcat Point USB 2.0 controller USB-A"); case 0x8cad8086: return ("Intel Wildcat Point USB 2.0 controller USB-B"); case 0x9c268086: return ("Intel Lynx Point LP USB 2.0 controller USB"); case 0x00e01033: return ("NEC uPD 720100 USB 2.0 controller"); case 0x006810de: return "NVIDIA nForce2 USB 2.0 controller"; case 0x008810de: return "NVIDIA nForce2 Ultra 400 USB 2.0 controller"; case 0x00d810de: return "NVIDIA nForce3 USB 2.0 controller"; case 0x00e810de: return "NVIDIA nForce3 250 USB 2.0 controller"; case 0x005b10de: return "NVIDIA nForce CK804 USB 2.0 controller"; case 0x036d10de: return "NVIDIA nForce MCP55 USB 2.0 controller"; case 0x03f210de: return "NVIDIA nForce MCP61 USB 2.0 controller"; case 0x0aa610de: return "NVIDIA nForce MCP79 USB 2.0 controller"; case 0x0aa910de: return "NVIDIA nForce MCP79 USB 2.0 controller"; case 0x0aaa10de: return "NVIDIA nForce MCP79 USB 2.0 controller"; case 0x15621131: return "Philips ISP156x USB 2.0 controller"; case 0x31041106: return ("VIA VT6202 USB 2.0 controller"); default: break; } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_EHCI)) { return ("EHCI (generic) USB 2.0 controller"); } return (NULL); /* dunno */ } static int ehci_pci_probe(device_t self) { const char *desc = ehci_pci_match(self); if (desc) { device_set_desc(self, desc); return (0); } else { return (ENXIO); } } static void ehci_pci_ati_quirk(device_t self, uint8_t is_sb700) { device_t smbdev; uint32_t val; if (is_sb700) { /* Lookup SMBUS PCI device */ smbdev = pci_find_device(PCI_EHCI_VENDORID_ATI, 0x4385); if (smbdev == NULL) return; val = pci_get_revid(smbdev); if (val != 0x3a && val != 0x3b) return; } /* * Note: this bit is described as reserved in SB700 * Register Reference Guide. */ val = pci_read_config(self, 0x53, 1); if (!(val & 0x8)) { val |= 0x8; pci_write_config(self, 0x53, val, 1); device_printf(self, "AMD SB600/700 quirk applied\n"); } } static void ehci_pci_via_quirk(device_t self) { uint32_t val; if ((pci_get_device(self) == 0x3104) && ((pci_get_revid(self) & 0xf0) == 0x60)) { /* Correct schedule sleep time to 10us */ val = pci_read_config(self, 0x4b, 1); if (val & 0x20) return; val |= 0x20; pci_write_config(self, 0x4b, val, 1); device_printf(self, "VIA-quirk applied\n"); } } static int ehci_pci_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } pci_enable_busmaster(self); switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) { case PCI_USB_REV_PRE_1_0: case PCI_USB_REV_1_0: case PCI_USB_REV_1_1: /* * NOTE: some EHCI USB controllers have the wrong USB * revision number. It appears those controllers are * fully compliant so we just ignore this value in * some common cases. */ device_printf(self, "pre-2.0 USB revision (ignored)\n"); /* fallthrough */ case PCI_USB_REV_2_0: break; default: /* Quirk for Parallels Desktop 4.0 */ device_printf(self, "USB revision is unknown. Assuming v2.0.\n"); break; } rid = PCI_CBMEM; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); /* * ehci_pci_match will never return NULL if ehci_pci_probe * succeeded */ device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_EHCI_VENDORID_ACERLABS: sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_EHCI_VENDORID_AMD: sprintf(sc->sc_vendor, "AMD"); break; case PCI_EHCI_VENDORID_APPLE: sprintf(sc->sc_vendor, "Apple"); break; case PCI_EHCI_VENDORID_ATI: sprintf(sc->sc_vendor, "ATI"); break; case PCI_EHCI_VENDORID_CMDTECH: sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_EHCI_VENDORID_INTEL: sprintf(sc->sc_vendor, "Intel"); break; case PCI_EHCI_VENDORID_NEC: sprintf(sc->sc_vendor, "NEC"); break; case PCI_EHCI_VENDORID_OPTI: sprintf(sc->sc_vendor, "OPTi"); break; case PCI_EHCI_VENDORID_PHILIPS: sprintf(sc->sc_vendor, "Philips"); break; case PCI_EHCI_VENDORID_SIS: sprintf(sc->sc_vendor, "SiS"); break; case PCI_EHCI_VENDORID_NVIDIA: case PCI_EHCI_VENDORID_NVIDIA2: sprintf(sc->sc_vendor, "nVidia"); break; case PCI_EHCI_VENDORID_VIA: sprintf(sc->sc_vendor, "VIA"); break; default: if (bootverbose) device_printf(self, "(New EHCI DeviceId=0x%08x)\n", pci_get_devid(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 *)ehci_interrupt, sc, &sc->sc_intr_hdl); #else err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); #endif if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } ehci_pci_take_controller(self); /* Undocumented quirks taken from Linux */ switch (pci_get_vendor(self)) { case PCI_EHCI_VENDORID_ATI: /* SB600 and SB700 EHCI quirk */ switch (pci_get_device(self)) { case 0x4386: ehci_pci_ati_quirk(self, 0); break; case 0x4396: ehci_pci_ati_quirk(self, 1); break; default: break; } break; case PCI_EHCI_VENDORID_VIA: ehci_pci_via_quirk(self); break; default: break; } /* Dropped interrupts workaround */ switch (pci_get_vendor(self)) { case PCI_EHCI_VENDORID_ATI: case PCI_EHCI_VENDORID_VIA: sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG; if (bootverbose) device_printf(self, "Dropped interrupts workaround enabled\n"); break; default: break; } /* Doorbell feature workaround */ switch (pci_get_vendor(self)) { case PCI_EHCI_VENDORID_NVIDIA: case PCI_EHCI_VENDORID_NVIDIA2: sc->sc_flags |= EHCI_SCFLG_IAADBUG; if (bootverbose) device_printf(self, "Doorbell workaround enabled\n"); break; default: break; } err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: ehci_pci_detach(self); return (ENXIO); } static int ehci_pci_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); device_t bdev; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); pci_disable_busmaster(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static int ehci_pci_take_controller(device_t self) { ehci_softc_t *sc = device_get_softc(self); uint32_t cparams; uint32_t eec; uint16_t to; uint8_t eecp; uint8_t bios_sem; cparams = EREAD4(sc, EHCI_HCCPARAMS); /* Synchronise with the BIOS if it owns the controller. */ for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; eecp = EHCI_EECP_NEXT(eec)) { eec = pci_read_config(self, eecp, 4); if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) { continue; } bios_sem = pci_read_config(self, eecp + EHCI_LEGSUP_BIOS_SEM, 1); if (bios_sem == 0) { continue; } device_printf(sc->sc_bus.bdev, "waiting for BIOS " "to give up control\n"); pci_write_config(self, eecp + EHCI_LEGSUP_OS_SEM, 1, 1); to = 500; while (1) { bios_sem = pci_read_config(self, eecp + EHCI_LEGSUP_BIOS_SEM, 1); if (bios_sem == 0) break; if (--to == 0) { device_printf(sc->sc_bus.bdev, "timed out waiting for BIOS\n"); break; } usb_pause_mtx(NULL, hz / 100); /* wait 10ms */ } } return (0); } static device_method_t ehci_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ehci_pci_probe), DEVMETHOD(device_attach, ehci_pci_attach), DEVMETHOD(device_detach, ehci_pci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(usb_take_controller, ehci_pci_take_controller), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_pci_methods, .size = sizeof(struct ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/musb_otg_atmelarm.c =================================================================== --- head/sys/dev/usb/controller/musb_otg_atmelarm.c (revision 276716) +++ head/sys/dev/usb/controller/musb_otg_atmelarm.c (revision 276717) @@ -1,265 +1,266 @@ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t musbotg_probe; static device_attach_t musbotg_attach; static device_detach_t musbotg_detach; struct musbotg_super_softc { struct musbotg_softc sc_otg; /* must be first */ }; static void musbotg_vbus_poll(struct musbotg_super_softc *sc) { uint8_t vbus_val = 1; /* fake VBUS on - TODO */ /* just forward it */ musbotg_vbus_interrupt(&sc->sc_otg, vbus_val); } static void musbotg_clocks_on(void *arg) { #if 0 struct musbotg_super_softc *sc = arg; #endif } static void musbotg_clocks_off(void *arg) { #if 0 struct musbotg_super_softc *sc = arg; #endif } static void musbotg_wrapper_interrupt(void *arg) { /* * Nothing to do. * Main driver takes care about everything */ musbotg_interrupt(arg, 0, 0, 0); } static void musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on) { /* * Nothing to do. * Main driver takes care about everything */ } static int musbotg_probe(device_t dev) { device_set_desc(dev, "MUSB OTG integrated USB controller"); return (0); } static int musbotg_attach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); int err; int rid; /* setup MUSB OTG USB controller interface softc */ sc->sc_otg.sc_clocks_on = &musbotg_clocks_on; sc->sc_otg.sc_clocks_off = &musbotg_clocks_off; sc->sc_otg.sc_clocks_arg = sc; /* initialise some bus fields */ sc->sc_otg.sc_bus.parent = dev; sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES; + sc->sc_otg.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_otg.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res); rid = 0; sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_irq_res)) { goto error; } sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_otg.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); sc->sc_otg.sc_id = 0; sc->sc_otg.sc_platform_data = sc; sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE; #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)musbotg_wrapper_interrupt, sc, &sc->sc_otg.sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (driver_intr_t *)musbotg_wrapper_interrupt, sc, &sc->sc_otg.sc_intr_hdl); #endif if (err) { sc->sc_otg.sc_intr_hdl = NULL; goto error; } err = musbotg_init(&sc->sc_otg); if (!err) { err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); } if (err) { goto error; } else { /* poll VBUS one time */ musbotg_vbus_poll(sc); } return (0); error: musbotg_detach(dev); return (ENXIO); } static int musbotg_detach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_otg.sc_bus.bdev) { bdev = sc->sc_otg.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { /* * only call musbotg_uninit() after musbotg_init() */ musbotg_uninit(&sc->sc_otg); err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, sc->sc_otg.sc_intr_hdl); sc->sc_otg.sc_intr_hdl = NULL; } /* free IRQ channel, if any */ if (sc->sc_otg.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_otg.sc_irq_res); sc->sc_otg.sc_irq_res = NULL; } /* free memory resource, if any */ if (sc->sc_otg.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); return (0); } static device_method_t musbotg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, musbotg_probe), DEVMETHOD(device_attach, musbotg_attach), DEVMETHOD(device_detach, musbotg_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t musbotg_driver = { .name = "musbotg", .methods = musbotg_methods, .size = sizeof(struct musbotg_super_softc), }; static devclass_t musbotg_devclass; DRIVER_MODULE(musbotg, atmelarm, musbotg_driver, musbotg_devclass, 0, 0); MODULE_DEPEND(musbotg, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ohci_atmelarm.c =================================================================== --- head/sys/dev/usb/controller/ohci_atmelarm.c (revision 276716) +++ head/sys/dev/usb/controller/ohci_atmelarm.c (revision 276717) @@ -1,241 +1,242 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 static device_probe_t ohci_atmelarm_probe; static device_attach_t ohci_atmelarm_attach; static device_detach_t ohci_atmelarm_detach; struct at91_ohci_softc { struct ohci_softc sc_ohci; /* must be first */ struct at91_pmc_clock *mclk; struct at91_pmc_clock *iclk; struct at91_pmc_clock *fclk; }; static int ohci_atmelarm_probe(device_t dev) { device_set_desc(dev, "AT91 integrated OHCI controller"); return (BUS_PROBE_DEFAULT); } static int ohci_atmelarm_attach(device_t dev) { struct at91_ohci_softc *sc = device_get_softc(dev); int err; int rid; /* initialise some bus fields */ sc->sc_ohci.sc_bus.parent = dev; sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_ohci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return (ENOMEM); } sc->mclk = at91_pmc_clock_ref("mck"); sc->iclk = at91_pmc_clock_ref("ohci_clk"); sc->fclk = at91_pmc_clock_ref("uhpck"); sc->sc_ohci.sc_dev = dev; rid = MEM_RID; sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); rid = 0; sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_irq_res)) { goto error; } sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_ohci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); strlcpy(sc->sc_ohci.sc_vendor, "Atmel", sizeof(sc->sc_ohci.sc_vendor)); err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); if (err) { sc->sc_ohci.sc_intr_hdl = NULL; goto error; } /* * turn on the clocks from the AT91's point of view. Keep the unit in reset. */ at91_pmc_clock_enable(sc->mclk); at91_pmc_clock_enable(sc->iclk); at91_pmc_clock_enable(sc->fclk); bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(&sc->sc_ohci); if (!err) { err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); } if (err) { goto error; } return (0); error: ohci_atmelarm_detach(dev); return (ENXIO); } static int ohci_atmelarm_detach(device_t dev) { struct at91_ohci_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_ohci.sc_bus.bdev) { bdev = sc->sc_ohci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); /* * Put the controller into reset, then disable clocks and do * the MI tear down. We have to disable the clocks/hardware * after we do the rest of the teardown. We also disable the * clocks in the opposite order we acquire them, but that * doesn't seem to be absolutely necessary. We free up the * clocks after we disable them, so the system could, in * theory, reuse them. */ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); at91_pmc_clock_disable(sc->fclk); at91_pmc_clock_disable(sc->iclk); at91_pmc_clock_disable(sc->mclk); at91_pmc_clock_deref(sc->fclk); at91_pmc_clock_deref(sc->iclk); at91_pmc_clock_deref(sc->mclk); if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(&sc->sc_ohci); err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); sc->sc_ohci.sc_intr_hdl = NULL; } if (sc->sc_ohci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); sc->sc_ohci.sc_irq_res = NULL; } if (sc->sc_ohci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_atmelarm_probe), DEVMETHOD(device_attach, ohci_atmelarm_attach), DEVMETHOD(device_detach, ohci_atmelarm_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_driver = { .name = "ohci", .methods = ohci_methods, .size = sizeof(struct at91_ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, atmelarm, ohci_driver, ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ohci_fdt.c =================================================================== --- head/sys/dev/usb/controller/ohci_fdt.c (revision 276716) +++ head/sys/dev/usb/controller/ohci_fdt.c (revision 276717) @@ -1,250 +1,251 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 static device_probe_t ohci_at91_fdt_probe; static device_attach_t ohci_at91_fdt_attach; static device_detach_t ohci_at91_fdt_detach; struct at91_ohci_softc { struct ohci_softc sc_ohci; /* must be first */ struct at91_pmc_clock *mclk; struct at91_pmc_clock *iclk; struct at91_pmc_clock *fclk; }; static int ohci_at91_fdt_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "atmel,at91rm9200-ohci")) return (ENXIO); device_set_desc(dev, "AT91 integrated OHCI controller"); return (BUS_PROBE_DEFAULT); } static int ohci_at91_fdt_attach(device_t dev) { struct at91_ohci_softc *sc = device_get_softc(dev); int err; int rid; /* initialise some bus fields */ sc->sc_ohci.sc_bus.parent = dev; sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_ohci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return (ENOMEM); } sc->mclk = at91_pmc_clock_ref("mck"); sc->iclk = at91_pmc_clock_ref("ohci_clk"); sc->fclk = at91_pmc_clock_ref("uhpck"); sc->sc_ohci.sc_dev = dev; rid = MEM_RID; sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); rid = 0; sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_irq_res)) { goto error; } sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_ohci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); strlcpy(sc->sc_ohci.sc_vendor, "Atmel", sizeof(sc->sc_ohci.sc_vendor)); err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); if (err) { sc->sc_ohci.sc_intr_hdl = NULL; goto error; } /* * turn on the clocks from the AT91's point of view. Keep the unit in reset. */ at91_pmc_clock_enable(sc->mclk); at91_pmc_clock_enable(sc->iclk); at91_pmc_clock_enable(sc->fclk); bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(&sc->sc_ohci); if (!err) { err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); } if (err) { goto error; } return (0); error: ohci_at91_fdt_detach(dev); return (ENXIO); } static int ohci_at91_fdt_detach(device_t dev) { struct at91_ohci_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_ohci.sc_bus.bdev) { bdev = sc->sc_ohci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_ohci.sc_io_res != NULL) { /* * Put the controller into reset, then disable clocks and do * the MI tear down. We have to disable the clocks/hardware * after we do the rest of the teardown. We also disable the * clocks in the opposite order we acquire them, but that * doesn't seem to be absolutely necessary. We free up the * clocks after we disable them, so the system could, in * theory, reuse them. */ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); at91_pmc_clock_disable(sc->fclk); at91_pmc_clock_disable(sc->iclk); at91_pmc_clock_disable(sc->mclk); at91_pmc_clock_deref(sc->fclk); at91_pmc_clock_deref(sc->iclk); at91_pmc_clock_deref(sc->mclk); if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(&sc->sc_ohci); err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); sc->sc_ohci.sc_intr_hdl = NULL; } if (sc->sc_ohci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); sc->sc_ohci.sc_irq_res = NULL; } if (sc->sc_ohci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_res = NULL; } } usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_at91_fdt_probe), DEVMETHOD(device_attach, ohci_at91_fdt_attach), DEVMETHOD(device_detach, ohci_at91_fdt_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_driver = { .name = "ohci", .methods = ohci_methods, .size = sizeof(struct at91_ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, simplebus, ohci_driver, ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ohci_pci.c =================================================================== --- head/sys/dev/usb/controller/ohci_pci.c (revision 276716) +++ head/sys/dev/usb/controller/ohci_pci.c (revision 276717) @@ -1,399 +1,400 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf */ /* The low level controller code for OHCI has been split into * PCI probes and OHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usb_if.h" #define PCI_OHCI_VENDORID_ACERLABS 0x10b9 #define PCI_OHCI_VENDORID_AMD 0x1022 #define PCI_OHCI_VENDORID_APPLE 0x106b #define PCI_OHCI_VENDORID_ATI 0x1002 #define PCI_OHCI_VENDORID_CMDTECH 0x1095 #define PCI_OHCI_VENDORID_NEC 0x1033 #define PCI_OHCI_VENDORID_NVIDIA 0x12D2 #define PCI_OHCI_VENDORID_NVIDIA2 0x10DE #define PCI_OHCI_VENDORID_OPTI 0x1045 #define PCI_OHCI_VENDORID_SIS 0x1039 #define PCI_OHCI_VENDORID_SUN 0x108e #define PCI_OHCI_BASE_REG 0x10 static device_probe_t ohci_pci_probe; static device_attach_t ohci_pci_attach; static device_detach_t ohci_pci_detach; static usb_take_controller_t ohci_pci_take_controller; static int ohci_pci_take_controller(device_t self) { uint32_t reg; uint32_t int_line; if (pci_get_powerstate(self) != PCI_POWERSTATE_D0) { device_printf(self, "chip is in D%d mode " "-- setting to D0\n", pci_get_powerstate(self)); reg = pci_read_config(self, PCI_CBMEM, 4); int_line = pci_read_config(self, PCIR_INTLINE, 4); pci_set_powerstate(self, PCI_POWERSTATE_D0); pci_write_config(self, PCI_CBMEM, reg, 4); pci_write_config(self, PCIR_INTLINE, int_line, 4); } return (0); } static const char * ohci_pci_match(device_t self) { uint32_t device_id = pci_get_devid(self); switch (device_id) { case 0x523710b9: return ("AcerLabs M5237 (Aladdin-V) USB controller"); case 0x740c1022: return ("AMD-756 USB Controller"); case 0x74141022: return ("AMD-766 USB Controller"); case 0x43741002: return "ATI SB400 USB Controller"; case 0x43751002: return "ATI SB400 USB Controller"; case 0x43971002: return ("AMD SB7x0/SB8x0/SB9x0 USB controller"); case 0x43981002: return ("AMD SB7x0/SB8x0/SB9x0 USB controller"); case 0x43991002: return ("AMD SB7x0/SB8x0/SB9x0 USB controller"); case 0x06701095: return ("CMD Tech 670 (USB0670) USB controller"); case 0x06731095: return ("CMD Tech 673 (USB0673) USB controller"); case 0xc8611045: return ("OPTi 82C861 (FireLink) USB controller"); case 0x00351033: return ("NEC uPD 9210 USB controller"); case 0x00d710de: return ("nVidia nForce3 USB Controller"); case 0x005a10de: return ("nVidia nForce CK804 USB Controller"); case 0x036c10de: return ("nVidia nForce MCP55 USB Controller"); case 0x03f110de: return ("nVidia nForce MCP61 USB Controller"); case 0x0aa510de: return ("nVidia nForce MCP79 USB Controller"); case 0x0aa710de: return ("nVidia nForce MCP79 USB Controller"); case 0x0aa810de: return ("nVidia nForce MCP79 USB Controller"); case 0x70011039: return ("SiS 5571 USB controller"); case 0x1103108e: return "Sun PCIO-2 USB controller"; case 0x0019106b: return ("Apple KeyLargo USB controller"); case 0x003f106b: return ("Apple KeyLargo/Intrepid USB controller"); default: break; } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_OHCI)) { return ("OHCI (generic) USB controller"); } return (NULL); } static int ohci_pci_probe(device_t self) { const char *desc = ohci_pci_match(self); if (desc) { device_set_desc(self, desc); return (0); } else { return (ENXIO); } } static int ohci_pci_attach(device_t self) { ohci_softc_t *sc = device_get_softc(self); int rid; int err; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ohci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_dev = self; pci_enable_busmaster(self); /* * Some Sun PCIO-2 USB controllers have their intpin register * bogusly set to 0, although it should be 4. Correct that. */ if (pci_get_devid(self) == 0x1103108e && pci_get_intpin(self) == 0) pci_set_intpin(self, 4); rid = PCI_CBMEM; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); /* * ohci_pci_match will never return NULL if ohci_pci_probe * succeeded */ device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_OHCI_VENDORID_ACERLABS: sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_OHCI_VENDORID_AMD: sprintf(sc->sc_vendor, "AMD"); break; case PCI_OHCI_VENDORID_APPLE: sprintf(sc->sc_vendor, "Apple"); break; case PCI_OHCI_VENDORID_ATI: sprintf(sc->sc_vendor, "ATI"); break; case PCI_OHCI_VENDORID_CMDTECH: sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_OHCI_VENDORID_NEC: sprintf(sc->sc_vendor, "NEC"); break; case PCI_OHCI_VENDORID_NVIDIA: case PCI_OHCI_VENDORID_NVIDIA2: sprintf(sc->sc_vendor, "nVidia"); break; case PCI_OHCI_VENDORID_OPTI: sprintf(sc->sc_vendor, "OPTi"); break; case PCI_OHCI_VENDORID_SIS: sprintf(sc->sc_vendor, "SiS"); break; case PCI_OHCI_VENDORID_SUN: sprintf(sc->sc_vendor, "SUN"); break; default: if (bootverbose) { device_printf(self, "(New OHCI DeviceId=0x%08x)\n", pci_get_devid(self)); } sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } /* sc->sc_bus.usbrev; set by ohci_init() */ #if (__FreeBSD_version >= 700031) err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_intr_hdl); #else err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_intr_hdl); #endif if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } err = ohci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed\n"); goto error; } return (0); error: ohci_pci_detach(self); return (ENXIO); } static int ohci_pci_detach(device_t self) { ohci_softc_t *sc = device_get_softc(self); device_t bdev; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); pci_disable_busmaster(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(sc); int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) { /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); } sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_pci_probe), DEVMETHOD(device_attach, ohci_pci_attach), DEVMETHOD(device_detach, ohci_pci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(usb_take_controller, ohci_pci_take_controller), DEVMETHOD_END }; static driver_t ohci_driver = { .name = "ohci", .methods = ohci_pci_methods, .size = sizeof(struct ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/ohci_s3c24x0.c =================================================================== --- head/sys/dev/usb/controller/ohci_s3c24x0.c (revision 276716) +++ head/sys/dev/usb/controller/ohci_s3c24x0.c (revision 276717) @@ -1,217 +1,218 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. * Copyright (c) 2009 Andrew Turner. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t ohci_s3c24x0_probe; static device_attach_t ohci_s3c24x0_attach; static device_detach_t ohci_s3c24x0_detach; static int ohci_s3c24x0_probe(device_t dev) { device_set_desc(dev, "S3C24x0 integrated OHCI controller"); return (BUS_PROBE_DEFAULT); } static int ohci_s3c24x0_attach(device_t dev) { struct ohci_softc *sc = device_get_softc(dev); int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_dev = dev; rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (!(sc->sc_io_res)) { err = ENOMEM; goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_irq_res)) { goto error; } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)ohci_interrupt, sc, &sc->sc_intr_hdl); if (err) { sc->sc_intr_hdl = NULL; goto error; } bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { goto error; } return (0); error: ohci_s3c24x0_detach(dev); return (ENXIO); } static int ohci_s3c24x0_detach(device_t dev) { struct ohci_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); /* * Put the controller into reset, then disable clocks and do * the MI tear down. We have to disable the clocks/hardware * after we do the rest of the teardown. We also disable the * clocks in the opposite order we acquire them, but that * doesn't seem to be absolutely necessary. We free up the * clocks after we disable them, so the system could, in * theory, reuse them. */ bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, OHCI_CONTROL, 0); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(sc); err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_s3c24x0_probe), DEVMETHOD(device_attach, ohci_s3c24x0_attach), DEVMETHOD(device_detach, ohci_s3c24x0_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_driver = { .name = "ohci", .methods = ohci_methods, .size = sizeof(struct ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, s3c24x0, ohci_driver, ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/saf1761_otg_boot.c =================================================================== --- head/sys/dev/usb/controller/saf1761_otg_boot.c (revision 276716) +++ head/sys/dev/usb/controller/saf1761_otg_boot.c (revision 276717) @@ -1,146 +1,147 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2014 Hans Petter Selasky * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include USB_GLOBAL_INCLUDE_FILE #include #include static device_probe_t saf1761_otg_fdt_probe; static device_attach_t saf1761_otg_fdt_attach; static device_detach_t saf1761_otg_fdt_detach; static device_method_t saf1761_otg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, saf1761_otg_fdt_probe), DEVMETHOD(device_attach, saf1761_otg_fdt_attach), DEVMETHOD(device_detach, saf1761_otg_fdt_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t saf1761_otg_driver = { .name = "saf1761otg", .methods = saf1761_otg_methods, .size = sizeof(struct saf1761_otg_softc), }; static devclass_t saf1761_otg_devclass; DRIVER_MODULE(saf1761otg, pci, saf1761_otg_driver, saf1761_otg_devclass, 0, 0); MODULE_DEPEND(saf1761otg, usb, 1, 1, 1); static int saf1761_otg_fdt_probe(device_t dev) { if (device_get_unit(dev) != 0) return (ENXIO); device_set_desc(dev, "ISP1761/SAF1761 DCI USB 2.0 Device Controller"); return (0); } static int saf1761_otg_fdt_attach(device_t dev) { struct saf1761_otg_softc *sc = device_get_softc(dev); int err; /* 32-bit data bus */ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DATA_BUS_WIDTH; /* initialise some bus fields */ sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = SOTG_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), NULL)) return (ENOMEM); sc->sc_io_res = (void *)1; sc->sc_io_tag = (void *)1; sc->sc_io_hdl = (void *)USB_PCI_MEMORY_ADDRESS; sc->sc_io_size = USB_PCI_MEMORY_SIZE; sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->sc_bus.bdev == NULL) goto error; device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_interrupt(dev, &saf1761_otg_filter_interrupt, &saf1761_otg_interrupt, sc); err = saf1761_otg_init(sc); if (err) { device_printf(dev, "Init failed\n"); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(dev, "USB probe and attach failed\n"); goto error; } return (0); error: saf1761_otg_fdt_detach(dev); return (ENXIO); } static int saf1761_otg_fdt_detach(device_t dev) { struct saf1761_otg_softc *sc = device_get_softc(dev); device_t bdev; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_irq_res) { /* * Only call uninit() after init() */ saf1761_otg_uninit(sc); } usb_bus_mem_free_all(&sc->sc_bus, NULL); return (0); } Index: head/sys/dev/usb/controller/saf1761_otg_fdt.c =================================================================== --- head/sys/dev/usb/controller/saf1761_otg_fdt.c (revision 276716) +++ head/sys/dev/usb/controller/saf1761_otg_fdt.c (revision 276717) @@ -1,274 +1,275 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2014 Hans Petter Selasky * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #include static device_probe_t saf1761_otg_fdt_probe; static device_attach_t saf1761_otg_fdt_attach; static device_detach_t saf1761_otg_fdt_detach; static device_method_t saf1761_otg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, saf1761_otg_fdt_probe), DEVMETHOD(device_attach, saf1761_otg_fdt_attach), DEVMETHOD(device_detach, saf1761_otg_fdt_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t saf1761_otg_driver = { .name = "saf1761otg", .methods = saf1761_otg_methods, .size = sizeof(struct saf1761_otg_softc), }; static devclass_t saf1761_otg_devclass; DRIVER_MODULE(saf1761otg, simplebus, saf1761_otg_driver, saf1761_otg_devclass, 0, 0); MODULE_DEPEND(saf1761otg, usb, 1, 1, 1); static int saf1761_otg_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "nxp,usb-isp1761")) return (ENXIO); device_set_desc(dev, "ISP1761/SAF1761 DCI USB 2.0 Device Controller"); return (0); } static int saf1761_otg_fdt_attach(device_t dev) { struct saf1761_otg_softc *sc = device_get_softc(dev); char param[24]; int err; int rid; /* get configuration from FDT */ /* get bus-width, if any */ if (OF_getprop(ofw_bus_get_node(dev), "bus-width", ¶m, sizeof(param)) > 0) { param[sizeof(param) - 1] = 0; if (strcmp(param, "32") == 0) sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DATA_BUS_WIDTH; } else { /* assume 32-bit data bus */ sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DATA_BUS_WIDTH; } /* get analog over-current setting */ if (OF_getprop(ofw_bus_get_node(dev), "analog-oc", ¶m, sizeof(param)) > 0) { sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_ANA_DIGI_OC; } /* get DACK polarity */ if (OF_getprop(ofw_bus_get_node(dev), "dack-polarity", ¶m, sizeof(param)) > 0) { sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DACK_POL; } /* get DREQ polarity */ if (OF_getprop(ofw_bus_get_node(dev), "dreq-polarity", ¶m, sizeof(param)) > 0) { sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_DREQ_POL; } /* get IRQ polarity */ if (OF_getprop(ofw_bus_get_node(dev), "int-polarity", ¶m, sizeof(param)) > 0) { sc->sc_interrupt_cfg |= SOTG_INTERRUPT_CFG_INTPOL; sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_INTR_POL; } /* get IRQ level triggering */ if (OF_getprop(ofw_bus_get_node(dev), "int-level", ¶m, sizeof(param)) > 0) { sc->sc_interrupt_cfg |= SOTG_INTERRUPT_CFG_INTLVL; sc->sc_hw_mode |= SOTG_HW_MODE_CTRL_INTR_LEVEL; } /* initialise some bus fields */ sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = SOTG_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_io_res == NULL) goto error; sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); /* try to allocate the HC interrupt first */ rid = 1; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { /* try to allocate a common IRQ second */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) goto error; } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->sc_bus.bdev == NULL) goto error; device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE, &saf1761_otg_filter_interrupt, &saf1761_otg_interrupt, sc, &sc->sc_intr_hdl); if (err) { sc->sc_intr_hdl = NULL; goto error; } err = saf1761_otg_init(sc); if (err) { device_printf(dev, "Init failed\n"); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(dev, "USB probe and attach failed\n"); goto error; } return (0); error: saf1761_otg_fdt_detach(dev); return (ENXIO); } static int saf1761_otg_fdt_detach(device_t dev) { struct saf1761_otg_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * Only call uninit() after init() */ saf1761_otg_uninit(sc); err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, NULL); return (0); } Index: head/sys/dev/usb/controller/uhci_pci.c =================================================================== --- head/sys/dev/usb/controller/uhci_pci.c (revision 276716) +++ head/sys/dev/usb/controller/uhci_pci.c (revision 276717) @@ -1,452 +1,453 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Universal Host Controller Interface * * UHCI spec: http://www.intel.com/ */ /* The low level controller code for UHCI has been split into * PCI probes and UHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usb_if.h" #define PCI_UHCI_VENDORID_INTEL 0x8086 #define PCI_UHCI_VENDORID_VIA 0x1106 /* PIIX4E has no separate stepping */ static device_probe_t uhci_pci_probe; static device_attach_t uhci_pci_attach; static device_detach_t uhci_pci_detach; static usb_take_controller_t uhci_pci_take_controller; static int uhci_pci_take_controller(device_t self) { pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); return (0); } static const char * uhci_pci_match(device_t self) { uint32_t device_id = pci_get_devid(self); switch (device_id) { case 0x26888086: return ("Intel 631XESB/632XESB/3100 USB controller USB-1"); case 0x26898086: return ("Intel 631XESB/632XESB/3100 USB controller USB-2"); case 0x268a8086: return ("Intel 631XESB/632XESB/3100 USB controller USB-3"); case 0x268b8086: return ("Intel 631XESB/632XESB/3100 USB controller USB-4"); case 0x70208086: return ("Intel 82371SB (PIIX3) USB controller"); case 0x71128086: return ("Intel 82371AB/EB (PIIX4) USB controller"); case 0x24128086: return ("Intel 82801AA (ICH) USB controller"); case 0x24228086: return ("Intel 82801AB (ICH0) USB controller"); case 0x24428086: return ("Intel 82801BA/BAM (ICH2) USB controller USB-A"); case 0x24448086: return ("Intel 82801BA/BAM (ICH2) USB controller USB-B"); case 0x24828086: return ("Intel 82801CA/CAM (ICH3) USB controller USB-A"); case 0x24848086: return ("Intel 82801CA/CAM (ICH3) USB controller USB-B"); case 0x24878086: return ("Intel 82801CA/CAM (ICH3) USB controller USB-C"); case 0x24c28086: return ("Intel 82801DB (ICH4) USB controller USB-A"); case 0x24c48086: return ("Intel 82801DB (ICH4) USB controller USB-B"); case 0x24c78086: return ("Intel 82801DB (ICH4) USB controller USB-C"); case 0x24d28086: return ("Intel 82801EB (ICH5) USB controller USB-A"); case 0x24d48086: return ("Intel 82801EB (ICH5) USB controller USB-B"); case 0x24d78086: return ("Intel 82801EB (ICH5) USB controller USB-C"); case 0x24de8086: return ("Intel 82801EB (ICH5) USB controller USB-D"); case 0x26588086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A"); case 0x26598086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-B"); case 0x265a8086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-C"); case 0x265b8086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D"); case 0x27c88086: return ("Intel 82801G (ICH7) USB controller USB-A"); case 0x27c98086: return ("Intel 82801G (ICH7) USB controller USB-B"); case 0x27ca8086: return ("Intel 82801G (ICH7) USB controller USB-C"); case 0x27cb8086: return ("Intel 82801G (ICH7) USB controller USB-D"); case 0x28308086: return ("Intel 82801H (ICH8) USB controller USB-A"); case 0x28318086: return ("Intel 82801H (ICH8) USB controller USB-B"); case 0x28328086: return ("Intel 82801H (ICH8) USB controller USB-C"); case 0x28348086: return ("Intel 82801H (ICH8) USB controller USB-D"); case 0x28358086: return ("Intel 82801H (ICH8) USB controller USB-E"); case 0x29348086: return ("Intel 82801I (ICH9) USB controller"); case 0x29358086: return ("Intel 82801I (ICH9) USB controller"); case 0x29368086: return ("Intel 82801I (ICH9) USB controller"); case 0x29378086: return ("Intel 82801I (ICH9) USB controller"); case 0x29388086: return ("Intel 82801I (ICH9) USB controller"); case 0x29398086: return ("Intel 82801I (ICH9) USB controller"); case 0x3a348086: return ("Intel 82801JI (ICH10) USB controller USB-A"); case 0x3a358086: return ("Intel 82801JI (ICH10) USB controller USB-B"); case 0x3a368086: return ("Intel 82801JI (ICH10) USB controller USB-C"); case 0x3a378086: return ("Intel 82801JI (ICH10) USB controller USB-D"); case 0x3a388086: return ("Intel 82801JI (ICH10) USB controller USB-E"); case 0x3a398086: return ("Intel 82801JI (ICH10) USB controller USB-F"); case 0x719a8086: return ("Intel 82443MX USB controller"); case 0x76028086: return ("Intel 82372FB/82468GX USB controller"); case 0x30381106: return ("VIA 83C572 USB controller"); default: break; } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_UHCI)) { return ("UHCI (generic) USB controller"); } return (NULL); } static int uhci_pci_probe(device_t self) { const char *desc = uhci_pci_match(self); if (desc) { device_set_desc(self, desc); return (0); } else { return (ENXIO); } } static int uhci_pci_attach(device_t self) { uhci_softc_t *sc = device_get_softc(self); int rid; int err; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = UHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &uhci_iterate_hw_softc)) { return ENOMEM; } sc->sc_dev = self; pci_enable_busmaster(self); rid = PCI_UHCI_BASE_REG; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map ports\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); /* disable interrupts */ bus_space_write_2(sc->sc_io_tag, sc->sc_io_hdl, UHCI_INTR, 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); /* * uhci_pci_match must never return NULL if uhci_pci_probe * succeeded */ device_set_desc(sc->sc_bus.bdev, uhci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_UHCI_VENDORID_INTEL: sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_VENDORID_VIA: sprintf(sc->sc_vendor, "VIA"); break; default: if (bootverbose) { device_printf(self, "(New UHCI DeviceId=0x%08x)\n", pci_get_devid(self)); } sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) { case PCI_USB_REV_PRE_1_0: sc->sc_bus.usbrev = USB_REV_PRE_1_0; break; case PCI_USB_REV_1_0: sc->sc_bus.usbrev = USB_REV_1_0; break; default: /* Quirk for Parallels Desktop 4.0 */ device_printf(self, "USB revision is unknown. Assuming v1.1.\n"); sc->sc_bus.usbrev = USB_REV_1_1; break; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)uhci_interrupt, sc, &sc->sc_intr_hdl); #else err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (driver_intr_t *)uhci_interrupt, sc, &sc->sc_intr_hdl); #endif if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* * Set the PIRQD enable bit and switch off all the others. We don't * want legacy support to interfere with us XXX Does this also mean * that the BIOS won't touch the keyboard anymore if it is connected * to the ports of the root hub? */ #ifdef USB_DEBUG if (pci_read_config(self, PCI_LEGSUP, 2) != PCI_LEGSUP_USBPIRQDEN) { device_printf(self, "LegSup = 0x%04x\n", pci_read_config(self, PCI_LEGSUP, 2)); } #endif pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); err = uhci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed\n"); goto error; } return (0); error: uhci_pci_detach(self); return (ENXIO); } int uhci_pci_detach(device_t self) { uhci_softc_t *sc = device_get_softc(self); device_t bdev; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); /* * disable interrupts that might have been switched on in * uhci_init. */ if (sc->sc_io_res) { USB_BUS_LOCK(&sc->sc_bus); /* stop the controller */ uhci_reset(sc); USB_BUS_UNLOCK(&sc->sc_bus); } pci_disable_busmaster(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) { /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); } sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &uhci_iterate_hw_softc); return (0); } static device_method_t uhci_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uhci_pci_probe), DEVMETHOD(device_attach, uhci_pci_attach), DEVMETHOD(device_detach, uhci_pci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(usb_take_controller, uhci_pci_take_controller), DEVMETHOD_END }; static driver_t uhci_driver = { .name = "uhci", .methods = uhci_pci_methods, .size = sizeof(struct uhci_softc), }; static devclass_t uhci_devclass; DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); MODULE_DEPEND(uhci, usb, 1, 1, 1); Index: head/sys/dev/usb/controller/usb_controller.c =================================================================== --- head/sys/dev/usb/controller/usb_controller.c (revision 276716) +++ head/sys/dev/usb/controller/usb_controller.c (revision 276717) @@ -1,990 +1,990 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_ctrl_debug #include #include #include #include #include #include #include #include #include #include #include "usb_if.h" #endif /* USB_GLOBAL_INCLUDE_FILE */ /* function prototypes */ static device_probe_t usb_probe; static device_attach_t usb_attach; static device_detach_t usb_detach; static device_suspend_t usb_suspend; static device_resume_t usb_resume; static device_shutdown_t usb_shutdown; static void usb_attach_sub(device_t, struct usb_bus *); /* static variables */ #ifdef USB_DEBUG static int usb_ctrl_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RWTUN, &usb_ctrl_debug, 0, "Debug level"); #endif #if USB_HAVE_ROOT_MOUNT_HOLD static int usb_no_boot_wait = 0; SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0, "No USB device enumerate waiting at boot."); #endif static int usb_no_suspend_wait = 0; SYSCTL_INT(_hw_usb, OID_AUTO, no_suspend_wait, CTLFLAG_RWTUN, &usb_no_suspend_wait, 0, "No USB device waiting at system suspend."); static int usb_no_shutdown_wait = 0; SYSCTL_INT(_hw_usb, OID_AUTO, no_shutdown_wait, CTLFLAG_RWTUN, &usb_no_shutdown_wait, 0, "No USB device waiting at system shutdown."); static devclass_t usb_devclass; static device_method_t usb_methods[] = { DEVMETHOD(device_probe, usb_probe), DEVMETHOD(device_attach, usb_attach), DEVMETHOD(device_detach, usb_detach), DEVMETHOD(device_suspend, usb_suspend), DEVMETHOD(device_resume, usb_resume), DEVMETHOD(device_shutdown, usb_shutdown), DEVMETHOD_END }; static driver_t usb_driver = { .name = "usbus", .methods = usb_methods, .size = 0, }; /* Host Only Drivers */ DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, xhci, usb_driver, usb_devclass, 0, 0); /* Device Only Drivers */ DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, musbotg, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, uss820dci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, octusb, usb_driver, usb_devclass, 0, 0); /* Dual Mode Drivers */ DRIVER_MODULE(usbus, dwcotg, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usbus, saf1761otg, usb_driver, usb_devclass, 0, 0); /*------------------------------------------------------------------------* * usb_probe * * This function is called from "{ehci,ohci,uhci}_pci_attach()". *------------------------------------------------------------------------*/ static int usb_probe(device_t dev) { DPRINTF("\n"); return (0); } #if USB_HAVE_ROOT_MOUNT_HOLD static void usb_root_mount_rel(struct usb_bus *bus) { if (bus->bus_roothold != NULL) { DPRINTF("Releasing root mount hold %p\n", bus->bus_roothold); root_mount_rel(bus->bus_roothold); bus->bus_roothold = NULL; } } #endif /*------------------------------------------------------------------------* * usb_attach *------------------------------------------------------------------------*/ static int usb_attach(device_t dev) { struct usb_bus *bus = device_get_ivars(dev); DPRINTF("\n"); if (bus == NULL) { device_printf(dev, "USB device has no ivars\n"); return (ENXIO); } #if USB_HAVE_ROOT_MOUNT_HOLD if (usb_no_boot_wait == 0) { /* delay vfs_mountroot until the bus is explored */ bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); } #endif usb_attach_sub(dev, bus); return (0); /* return success */ } /*------------------------------------------------------------------------* * usb_detach *------------------------------------------------------------------------*/ static int usb_detach(device_t dev) { struct usb_bus *bus = device_get_softc(dev); DPRINTF("\n"); if (bus == NULL) { /* was never setup properly */ return (0); } /* Stop power watchdog */ usb_callout_drain(&bus->power_wdog); #if USB_HAVE_ROOT_MOUNT_HOLD /* Let the USB explore process detach all devices. */ usb_root_mount_rel(bus); #endif USB_BUS_LOCK(bus); /* Queue detach job */ usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->detach_msg[0], &bus->detach_msg[1]); /* Wait for detach to complete */ usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), &bus->detach_msg[0], &bus->detach_msg[1]); USB_BUS_UNLOCK(bus); #if USB_HAVE_PER_BUS_PROCESS /* Get rid of USB callback processes */ usb_proc_free(USB_BUS_GIANT_PROC(bus)); usb_proc_free(USB_BUS_NON_GIANT_PROC(bus)); /* Get rid of USB explore process */ usb_proc_free(USB_BUS_EXPLORE_PROC(bus)); /* Get rid of control transfer process */ usb_proc_free(USB_BUS_CONTROL_XFER_PROC(bus)); #endif #if USB_HAVE_PF usbpf_detach(bus); #endif return (0); } /*------------------------------------------------------------------------* * usb_suspend *------------------------------------------------------------------------*/ static int usb_suspend(device_t dev) { struct usb_bus *bus = device_get_softc(dev); DPRINTF("\n"); if (bus == NULL) { /* was never setup properly */ return (0); } USB_BUS_LOCK(bus); usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->suspend_msg[0], &bus->suspend_msg[1]); if (usb_no_suspend_wait == 0) { /* wait for suspend callback to be executed */ usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), &bus->suspend_msg[0], &bus->suspend_msg[1]); } USB_BUS_UNLOCK(bus); return (0); } /*------------------------------------------------------------------------* * usb_resume *------------------------------------------------------------------------*/ static int usb_resume(device_t dev) { struct usb_bus *bus = device_get_softc(dev); DPRINTF("\n"); if (bus == NULL) { /* was never setup properly */ return (0); } USB_BUS_LOCK(bus); usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->resume_msg[0], &bus->resume_msg[1]); USB_BUS_UNLOCK(bus); return (0); } /*------------------------------------------------------------------------* * usb_bus_reset_async_locked *------------------------------------------------------------------------*/ void usb_bus_reset_async_locked(struct usb_bus *bus) { USB_BUS_LOCK_ASSERT(bus, MA_OWNED); DPRINTF("\n"); if (bus->reset_msg[0].hdr.pm_qentry.tqe_prev != NULL || bus->reset_msg[1].hdr.pm_qentry.tqe_prev != NULL) { DPRINTF("Reset already pending\n"); return; } device_printf(bus->parent, "Resetting controller\n"); usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->reset_msg[0], &bus->reset_msg[1]); } /*------------------------------------------------------------------------* * usb_shutdown *------------------------------------------------------------------------*/ static int usb_shutdown(device_t dev) { struct usb_bus *bus = device_get_softc(dev); DPRINTF("\n"); if (bus == NULL) { /* was never setup properly */ return (0); } DPRINTF("%s: Controller shutdown\n", device_get_nameunit(bus->bdev)); USB_BUS_LOCK(bus); usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->shutdown_msg[0], &bus->shutdown_msg[1]); if (usb_no_shutdown_wait == 0) { /* wait for shutdown callback to be executed */ usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), &bus->shutdown_msg[0], &bus->shutdown_msg[1]); } USB_BUS_UNLOCK(bus); DPRINTF("%s: Controller shutdown complete\n", device_get_nameunit(bus->bdev)); return (0); } /*------------------------------------------------------------------------* * usb_bus_explore * * This function is used to explore the device tree from the root. *------------------------------------------------------------------------*/ static void usb_bus_explore(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *udev; bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; if (bus->no_explore != 0) return; if (udev != NULL) { USB_BUS_UNLOCK(bus); uhub_explore_handle_re_enumerate(udev); USB_BUS_LOCK(bus); } if (udev != NULL && udev->hub != NULL) { if (bus->do_probe) { bus->do_probe = 0; bus->driver_added_refcount++; } if (bus->driver_added_refcount == 0) { /* avoid zero, hence that is memory default */ bus->driver_added_refcount = 1; } #ifdef DDB /* * The following three lines of code are only here to * recover from DDB: */ usb_proc_rewakeup(USB_BUS_CONTROL_XFER_PROC(bus)); usb_proc_rewakeup(USB_BUS_GIANT_PROC(bus)); usb_proc_rewakeup(USB_BUS_NON_GIANT_PROC(bus)); #endif USB_BUS_UNLOCK(bus); #if USB_HAVE_POWERD /* * First update the USB power state! */ usb_bus_powerd(bus); #endif /* Explore the Root USB HUB. */ (udev->hub->explore) (udev); USB_BUS_LOCK(bus); } #if USB_HAVE_ROOT_MOUNT_HOLD usb_root_mount_rel(bus); #endif } /*------------------------------------------------------------------------* * usb_bus_detach * * This function is used to detach the device tree from the root. *------------------------------------------------------------------------*/ static void usb_bus_detach(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *udev; device_t dev; bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; dev = bus->bdev; /* clear the softc */ device_set_softc(dev, NULL); USB_BUS_UNLOCK(bus); /* detach children first */ mtx_lock(&Giant); bus_generic_detach(dev); mtx_unlock(&Giant); /* * Free USB device and all subdevices, if any. */ usb_free_device(udev, 0); USB_BUS_LOCK(bus); /* clear bdev variable last */ bus->bdev = NULL; } /*------------------------------------------------------------------------* * usb_bus_suspend * * This function is used to suspend the USB controller. *------------------------------------------------------------------------*/ static void usb_bus_suspend(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *udev; usb_error_t err; uint8_t do_unlock; DPRINTF("\n"); bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; if (udev == NULL || bus->bdev == NULL) return; USB_BUS_UNLOCK(bus); /* * We use the shutdown event here because the suspend and * resume events are reserved for the USB port suspend and * resume. The USB system suspend is implemented like full * shutdown and all connected USB devices will be disconnected * subsequently. At resume all USB devices will be * re-connected again. */ bus_generic_shutdown(bus->bdev); do_unlock = usbd_enum_lock(udev); err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); if (err) device_printf(bus->bdev, "Could not unconfigure root HUB\n"); USB_BUS_LOCK(bus); bus->hw_power_state = 0; bus->no_explore = 1; USB_BUS_UNLOCK(bus); if (bus->methods->set_hw_power != NULL) (bus->methods->set_hw_power) (bus); if (bus->methods->set_hw_power_sleep != NULL) (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SUSPEND); if (do_unlock) usbd_enum_unlock(udev); USB_BUS_LOCK(bus); } /*------------------------------------------------------------------------* * usb_bus_resume * * This function is used to resume the USB controller. *------------------------------------------------------------------------*/ static void usb_bus_resume(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *udev; usb_error_t err; uint8_t do_unlock; DPRINTF("\n"); bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; if (udev == NULL || bus->bdev == NULL) return; USB_BUS_UNLOCK(bus); do_unlock = usbd_enum_lock(udev); #if 0 DEVMETHOD(usb_take_controller, NULL); /* dummy */ #endif USB_TAKE_CONTROLLER(device_get_parent(bus->bdev)); USB_BUS_LOCK(bus); bus->hw_power_state = USB_HW_POWER_CONTROL | USB_HW_POWER_BULK | USB_HW_POWER_INTERRUPT | USB_HW_POWER_ISOC | USB_HW_POWER_NON_ROOT_HUB; bus->no_explore = 0; USB_BUS_UNLOCK(bus); if (bus->methods->set_hw_power_sleep != NULL) (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_RESUME); if (bus->methods->set_hw_power != NULL) (bus->methods->set_hw_power) (bus); /* restore USB configuration to index 0 */ err = usbd_set_config_index(udev, 0); if (err) device_printf(bus->bdev, "Could not configure root HUB\n"); /* probe and attach */ err = usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY); if (err) { device_printf(bus->bdev, "Could not probe and " "attach root HUB\n"); } if (do_unlock) usbd_enum_unlock(udev); USB_BUS_LOCK(bus); } /*------------------------------------------------------------------------* * usb_bus_reset * * This function is used to reset the USB controller. *------------------------------------------------------------------------*/ static void usb_bus_reset(struct usb_proc_msg *pm) { struct usb_bus *bus; DPRINTF("\n"); bus = ((struct usb_bus_msg *)pm)->bus; if (bus->bdev == NULL || bus->no_explore != 0) return; /* a suspend and resume will reset the USB controller */ usb_bus_suspend(pm); usb_bus_resume(pm); } /*------------------------------------------------------------------------* * usb_bus_shutdown * * This function is used to shutdown the USB controller. *------------------------------------------------------------------------*/ static void usb_bus_shutdown(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *udev; usb_error_t err; uint8_t do_unlock; bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; if (udev == NULL || bus->bdev == NULL) return; USB_BUS_UNLOCK(bus); bus_generic_shutdown(bus->bdev); do_unlock = usbd_enum_lock(udev); err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); if (err) device_printf(bus->bdev, "Could not unconfigure root HUB\n"); USB_BUS_LOCK(bus); bus->hw_power_state = 0; bus->no_explore = 1; USB_BUS_UNLOCK(bus); if (bus->methods->set_hw_power != NULL) (bus->methods->set_hw_power) (bus); if (bus->methods->set_hw_power_sleep != NULL) (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SHUTDOWN); if (do_unlock) usbd_enum_unlock(udev); USB_BUS_LOCK(bus); } static void usb_power_wdog(void *arg) { struct usb_bus *bus = arg; USB_BUS_LOCK_ASSERT(bus, MA_OWNED); usb_callout_reset(&bus->power_wdog, 4 * hz, usb_power_wdog, arg); #ifdef DDB /* * The following line of code is only here to recover from * DDB: */ usb_proc_rewakeup(USB_BUS_EXPLORE_PROC(bus)); /* recover from DDB */ #endif #if USB_HAVE_POWERD USB_BUS_UNLOCK(bus); usb_bus_power_update(bus); USB_BUS_LOCK(bus); #endif } /*------------------------------------------------------------------------* * usb_bus_attach * * This function attaches USB in context of the explore thread. *------------------------------------------------------------------------*/ static void usb_bus_attach(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *child; device_t dev; usb_error_t err; enum usb_dev_speed speed; bus = ((struct usb_bus_msg *)pm)->bus; dev = bus->bdev; DPRINTF("\n"); switch (bus->usbrev) { case USB_REV_1_0: speed = USB_SPEED_FULL; device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); break; case USB_REV_1_1: speed = USB_SPEED_FULL; device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); break; case USB_REV_2_0: speed = USB_SPEED_HIGH; device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); break; case USB_REV_2_5: speed = USB_SPEED_VARIABLE; device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); break; case USB_REV_3_0: speed = USB_SPEED_SUPER; device_printf(bus->bdev, "5.0Gbps Super Speed USB v3.0\n"); break; default: device_printf(bus->bdev, "Unsupported USB revision\n"); #if USB_HAVE_ROOT_MOUNT_HOLD usb_root_mount_rel(bus); #endif return; } /* default power_mask value */ bus->hw_power_state = USB_HW_POWER_CONTROL | USB_HW_POWER_BULK | USB_HW_POWER_INTERRUPT | USB_HW_POWER_ISOC | USB_HW_POWER_NON_ROOT_HUB; USB_BUS_UNLOCK(bus); /* make sure power is set at least once */ if (bus->methods->set_hw_power != NULL) { (bus->methods->set_hw_power) (bus); } /* allocate the Root USB device */ child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, speed, USB_MODE_HOST); if (child) { err = usb_probe_and_attach(child, USB_IFACE_INDEX_ANY); if (!err) { if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { err = USB_ERR_NO_ROOT_HUB; } } } else { err = USB_ERR_NOMEM; } USB_BUS_LOCK(bus); if (err) { device_printf(bus->bdev, "Root HUB problem, error=%s\n", usbd_errstr(err)); #if USB_HAVE_ROOT_MOUNT_HOLD usb_root_mount_rel(bus); #endif } /* set softc - we are ready */ device_set_softc(dev, bus); /* start watchdog */ usb_power_wdog(bus); } /*------------------------------------------------------------------------* * usb_attach_sub * * This function creates a thread which runs the USB attach code. *------------------------------------------------------------------------*/ static void usb_attach_sub(device_t dev, struct usb_bus *bus) { mtx_lock(&Giant); if (usb_devclass_ptr == NULL) usb_devclass_ptr = devclass_find("usbus"); mtx_unlock(&Giant); #if USB_HAVE_PF usbpf_attach(bus); #endif /* Initialise USB process messages */ bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; bus->explore_msg[0].bus = bus; bus->explore_msg[1].hdr.pm_callback = &usb_bus_explore; bus->explore_msg[1].bus = bus; bus->detach_msg[0].hdr.pm_callback = &usb_bus_detach; bus->detach_msg[0].bus = bus; bus->detach_msg[1].hdr.pm_callback = &usb_bus_detach; bus->detach_msg[1].bus = bus; bus->attach_msg[0].hdr.pm_callback = &usb_bus_attach; bus->attach_msg[0].bus = bus; bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach; bus->attach_msg[1].bus = bus; bus->suspend_msg[0].hdr.pm_callback = &usb_bus_suspend; bus->suspend_msg[0].bus = bus; bus->suspend_msg[1].hdr.pm_callback = &usb_bus_suspend; bus->suspend_msg[1].bus = bus; bus->resume_msg[0].hdr.pm_callback = &usb_bus_resume; bus->resume_msg[0].bus = bus; bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume; bus->resume_msg[1].bus = bus; bus->reset_msg[0].hdr.pm_callback = &usb_bus_reset; bus->reset_msg[0].bus = bus; bus->reset_msg[1].hdr.pm_callback = &usb_bus_reset; bus->reset_msg[1].bus = bus; bus->shutdown_msg[0].hdr.pm_callback = &usb_bus_shutdown; bus->shutdown_msg[0].bus = bus; bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown; bus->shutdown_msg[1].bus = bus; #if USB_HAVE_PER_BUS_PROCESS /* Create USB explore and callback processes */ if (usb_proc_create(USB_BUS_GIANT_PROC(bus), &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { device_printf(dev, "WARNING: Creation of USB Giant " "callback process failed.\n"); } else if (usb_proc_create(USB_BUS_NON_GIANT_PROC(bus), &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_HIGH)) { device_printf(dev, "WARNING: Creation of USB non-Giant " "callback process failed.\n"); } else if (usb_proc_create(USB_BUS_EXPLORE_PROC(bus), &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { device_printf(dev, "WARNING: Creation of USB explore " "process failed.\n"); } else if (usb_proc_create(USB_BUS_CONTROL_XFER_PROC(bus), &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { device_printf(dev, "WARNING: Creation of USB control transfer " "process failed.\n"); } else #endif { /* Get final attach going */ USB_BUS_LOCK(bus); usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->attach_msg[0], &bus->attach_msg[1]); USB_BUS_UNLOCK(bus); /* Do initial explore */ usb_needs_explore(bus, 1); } } SYSUNINIT(usb_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb_bus_unload, NULL); /*------------------------------------------------------------------------* * usb_bus_mem_flush_all_cb *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA static void usb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, struct usb_page *pg, usb_size_t size, usb_size_t align) { usb_pc_cpu_flush(pc); } #endif /*------------------------------------------------------------------------* * usb_bus_mem_flush_all - factored out code *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA void usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) { if (cb) { cb(bus, &usb_bus_mem_flush_all_cb); } } #endif /*------------------------------------------------------------------------* * usb_bus_mem_alloc_all_cb *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA static void usb_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, struct usb_page *pg, usb_size_t size, usb_size_t align) { /* need to initialize the page cache */ pc->tag_parent = bus->dma_parent_tag; if (usb_pc_alloc_mem(pc, pg, size, align)) { bus->alloc_failed = 1; } } #endif /*------------------------------------------------------------------------* * usb_bus_mem_alloc_all - factored out code * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, usb_bus_mem_cb_t *cb) { bus->alloc_failed = 0; mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), "usb_def_mtx", MTX_DEF | MTX_RECURSE); mtx_init(&bus->bus_spin_lock, device_get_nameunit(bus->parent), "usb_spin_mtx", MTX_SPIN | MTX_RECURSE); usb_callout_init_mtx(&bus->power_wdog, &bus->bus_mtx, 0); TAILQ_INIT(&bus->intr_q.head); #if USB_HAVE_BUSDMA usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, - dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX); + dmat, &bus->bus_mtx, NULL, bus->dma_bits, USB_BUS_DMA_TAG_MAX); #endif if ((bus->devices_max > USB_MAX_DEVICES) || (bus->devices_max < USB_MIN_DEVICES) || (bus->devices == NULL)) { DPRINTFN(0, "Devices field has not been " "initialised properly\n"); bus->alloc_failed = 1; /* failure */ } #if USB_HAVE_BUSDMA if (cb) { cb(bus, &usb_bus_mem_alloc_all_cb); } #endif if (bus->alloc_failed) { usb_bus_mem_free_all(bus, cb); } return (bus->alloc_failed); } /*------------------------------------------------------------------------* * usb_bus_mem_free_all_cb *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA static void usb_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, struct usb_page *pg, usb_size_t size, usb_size_t align) { usb_pc_free_mem(pc); } #endif /*------------------------------------------------------------------------* * usb_bus_mem_free_all - factored out code *------------------------------------------------------------------------*/ void usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) { #if USB_HAVE_BUSDMA if (cb) { cb(bus, &usb_bus_mem_free_all_cb); } usb_dma_tag_unsetup(bus->dma_parent_tag); #endif mtx_destroy(&bus->bus_mtx); mtx_destroy(&bus->bus_spin_lock); } /* convenience wrappers */ void usb_proc_explore_mwait(struct usb_device *udev, void *pm1, void *pm2) { usb_proc_mwait(USB_BUS_EXPLORE_PROC(udev->bus), pm1, pm2); } void * usb_proc_explore_msignal(struct usb_device *udev, void *pm1, void *pm2) { return (usb_proc_msignal(USB_BUS_EXPLORE_PROC(udev->bus), pm1, pm2)); } void usb_proc_explore_lock(struct usb_device *udev) { USB_BUS_LOCK(udev->bus); } void usb_proc_explore_unlock(struct usb_device *udev) { USB_BUS_UNLOCK(udev->bus); } Index: head/sys/dev/usb/controller/uss820dci_atmelarm.c =================================================================== --- head/sys/dev/usb/controller/uss820dci_atmelarm.c (revision 276716) +++ head/sys/dev/usb/controller/uss820dci_atmelarm.c (revision 276717) @@ -1,200 +1,201 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2008 Hans Petter Selasky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t uss820_atmelarm_probe; static device_attach_t uss820_atmelarm_attach; static device_detach_t uss820_atmelarm_detach; static device_method_t uss820dci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uss820_atmelarm_probe), DEVMETHOD(device_attach, uss820_atmelarm_attach), DEVMETHOD(device_detach, uss820_atmelarm_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t uss820dci_driver = { .name = "uss820dci", .methods = uss820dci_methods, .size = sizeof(struct uss820dci_softc), }; static devclass_t uss820dci_devclass; DRIVER_MODULE(uss820dci, atmelarm, uss820dci_driver, uss820dci_devclass, 0, 0); MODULE_DEPEND(uss820dci, usb, 1, 1, 1); static const char *const uss820_desc = "USS820 USB Device Controller"; static int uss820_atmelarm_probe(device_t dev) { device_set_desc(dev, uss820_desc); return (0); /* success */ } static int uss820_atmelarm_attach(device_t dev) { struct uss820dci_softc *sc = device_get_softc(dev); int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = USS820_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (!sc->sc_io_res) { goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { goto error; } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_TTY | INTR_MPSAFE, uss820dci_filter_interrupt, uss820dci_interrupt, sc, &sc->sc_intr_hdl); if (err) { sc->sc_intr_hdl = NULL; goto error; } err = uss820dci_init(sc); if (err) { device_printf(dev, "Init failed\n"); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(dev, "USB probe and attach failed\n"); goto error; } return (0); error: uss820_atmelarm_detach(dev); return (ENXIO); } static int uss820_atmelarm_detach(device_t dev) { struct uss820dci_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call at91_udp_uninit() after at91_udp_init() */ uss820dci_uninit(sc); err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, NULL); return (0); } Index: head/sys/dev/usb/controller/xhci.c =================================================================== --- head/sys/dev/usb/controller/xhci.c (revision 276716) +++ head/sys/dev/usb/controller/xhci.c (revision 276717) @@ -1,4296 +1,4306 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * USB eXtensible Host Controller Interface, a.k.a. USB 3.0 controller. * * The XHCI 1.0 spec can be found at * http://www.intel.com/technology/usb/download/xHCI_Specification_for_USB.pdf * and the USB 3.0 spec at * http://www.usb.org/developers/docs/usb_30_spec_060910.zip */ /* * A few words about the design implementation: This driver emulates * the concept about TDs which is found in EHCI specification. This * way we achieve that the USB controller drivers look similar to * eachother which makes it easier to understand the code. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR xhcidebug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #include #define XHCI_BUS2SC(bus) \ ((struct xhci_softc *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus)))) static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); static int xhcistreams; SYSCTL_INT(_hw_usb_xhci, OID_AUTO, streams, CTLFLAG_RWTUN, &xhcistreams, 0, "Set to enable streams mode support"); #ifdef USB_DEBUG static int xhcidebug; static int xhciroute; static int xhcipolling; SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RWTUN, &xhcidebug, 0, "Debug level"); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, xhci_port_route, CTLFLAG_RWTUN, &xhciroute, 0, "Routing bitmap for switching EHCI ports to XHCI controller"); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RWTUN, &xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller"); #else #define xhciroute 0 #endif #define XHCI_INTR_ENDPT 1 struct xhci_std_temp { struct xhci_softc *sc; struct usb_page_cache *pc; struct xhci_td *td; struct xhci_td *td_next; uint32_t len; uint32_t offset; uint32_t max_packet_size; uint32_t average; uint16_t isoc_delta; uint16_t isoc_frame; uint8_t shortpkt; uint8_t multishort; uint8_t last_frame; uint8_t trb_type; uint8_t direction; uint8_t tbc; uint8_t tlbpc; uint8_t step_td; uint8_t do_isoc_sync; }; static void xhci_do_poll(struct usb_bus *); static void xhci_device_done(struct usb_xfer *, usb_error_t); static void xhci_root_intr(struct xhci_softc *); static void xhci_free_device_ext(struct usb_device *); static struct xhci_endpoint_ext *xhci_get_endpoint_ext(struct usb_device *, struct usb_endpoint_descriptor *); static usb_proc_callback_t xhci_configure_msg; static usb_error_t xhci_configure_device(struct usb_device *); static usb_error_t xhci_configure_endpoint(struct usb_device *, struct usb_endpoint_descriptor *, struct xhci_endpoint_ext *, uint16_t, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t, uint8_t); static usb_error_t xhci_configure_mask(struct usb_device *, uint32_t, uint8_t); static usb_error_t xhci_cmd_evaluate_ctx(struct xhci_softc *, uint64_t, uint8_t); static void xhci_endpoint_doorbell(struct usb_xfer *); static void xhci_ctx_set_le32(struct xhci_softc *sc, volatile uint32_t *ptr, uint32_t val); static uint32_t xhci_ctx_get_le32(struct xhci_softc *sc, volatile uint32_t *ptr); static void xhci_ctx_set_le64(struct xhci_softc *sc, volatile uint64_t *ptr, uint64_t val); #ifdef USB_DEBUG static uint64_t xhci_ctx_get_le64(struct xhci_softc *sc, volatile uint64_t *ptr); #endif static const struct usb_bus_methods xhci_bus_methods; #ifdef USB_DEBUG static void xhci_dump_trb(struct xhci_trb *trb) { DPRINTFN(5, "trb = %p\n", trb); DPRINTFN(5, "qwTrb0 = 0x%016llx\n", (long long)le64toh(trb->qwTrb0)); DPRINTFN(5, "dwTrb2 = 0x%08x\n", le32toh(trb->dwTrb2)); DPRINTFN(5, "dwTrb3 = 0x%08x\n", le32toh(trb->dwTrb3)); } static void xhci_dump_endpoint(struct xhci_softc *sc, struct xhci_endp_ctx *pep) { DPRINTFN(5, "pep = %p\n", pep); DPRINTFN(5, "dwEpCtx0=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx0)); DPRINTFN(5, "dwEpCtx1=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx1)); DPRINTFN(5, "qwEpCtx2=0x%016llx\n", (long long)xhci_ctx_get_le64(sc, &pep->qwEpCtx2)); DPRINTFN(5, "dwEpCtx4=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx4)); DPRINTFN(5, "dwEpCtx5=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx5)); DPRINTFN(5, "dwEpCtx6=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx6)); DPRINTFN(5, "dwEpCtx7=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx7)); } static void xhci_dump_device(struct xhci_softc *sc, struct xhci_slot_ctx *psl) { DPRINTFN(5, "psl = %p\n", psl); DPRINTFN(5, "dwSctx0=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx0)); DPRINTFN(5, "dwSctx1=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx1)); DPRINTFN(5, "dwSctx2=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx2)); DPRINTFN(5, "dwSctx3=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx3)); } #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) { struct xhci_softc *sc = XHCI_BUS2SC(bus); uint8_t i; cb(bus, &sc->sc_hw.root_pc, &sc->sc_hw.root_pg, sizeof(struct xhci_hw_root), XHCI_PAGE_SIZE); cb(bus, &sc->sc_hw.ctx_pc, &sc->sc_hw.ctx_pg, sizeof(struct xhci_dev_ctx_addr), XHCI_PAGE_SIZE); for (i = 0; i != XHCI_MAX_SCRATCHPADS; i++) { cb(bus, &sc->sc_hw.scratch_pc[i], &sc->sc_hw.scratch_pg[i], XHCI_PAGE_SIZE, XHCI_PAGE_SIZE); } } static void xhci_ctx_set_le32(struct xhci_softc *sc, volatile uint32_t *ptr, uint32_t val) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint32_t *)(((volatile uint8_t *)ptr) + offset); } *ptr = htole32(val); } static uint32_t xhci_ctx_get_le32(struct xhci_softc *sc, volatile uint32_t *ptr) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint32_t *)(((volatile uint8_t *)ptr) + offset); } return (le32toh(*ptr)); } static void xhci_ctx_set_le64(struct xhci_softc *sc, volatile uint64_t *ptr, uint64_t val) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint64_t *)(((volatile uint8_t *)ptr) + offset); } *ptr = htole64(val); } #ifdef USB_DEBUG static uint64_t xhci_ctx_get_le64(struct xhci_softc *sc, volatile uint64_t *ptr) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint64_t *)(((volatile uint8_t *)ptr) + offset); } return (le64toh(*ptr)); } #endif static int xhci_reset_command_queue_locked(struct xhci_softc *sc) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; uint64_t addr; uint32_t temp; DPRINTF("\n"); temp = XREAD4(sc, oper, XHCI_CRCR_LO); if (temp & XHCI_CRCR_LO_CRR) { DPRINTF("Command ring running\n"); temp &= ~(XHCI_CRCR_LO_CS | XHCI_CRCR_LO_CA); /* * Try to abort the last command as per section * 4.6.1.2 "Aborting a Command" of the XHCI * specification: */ /* stop and cancel */ XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CS); XWRITE4(sc, oper, XHCI_CRCR_HI, 0); XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CA); XWRITE4(sc, oper, XHCI_CRCR_HI, 0); /* wait 250ms */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 4); /* check if command ring is still running */ temp = XREAD4(sc, oper, XHCI_CRCR_LO); if (temp & XHCI_CRCR_LO_CRR) { DPRINTF("Comand ring still running\n"); return (USB_ERR_IOERROR); } } /* reset command ring */ sc->sc_command_ccs = 1; sc->sc_command_idx = 0; usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); - /* setup command ring control base address */ + /* set up command ring control base address */ addr = buf_res.physaddr; phwr = buf_res.buffer; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0]; DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr); memset(phwr->hwr_commands, 0, sizeof(phwr->hwr_commands)); phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr); usb_pc_cpu_flush(&sc->sc_hw.root_pc); XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS); XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32)); return (0); } usb_error_t xhci_start_controller(struct xhci_softc *sc) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; struct xhci_dev_ctx_addr *pdctxa; uint64_t addr; uint32_t temp; uint16_t i; DPRINTF("\n"); - sc->sc_capa_off = 0; - sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH); - sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F; - sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3; - - DPRINTF("CAPLENGTH=0x%x\n", sc->sc_oper_off); - DPRINTF("RUNTIMEOFFSET=0x%x\n", sc->sc_runt_off); - DPRINTF("DOOROFFSET=0x%x\n", sc->sc_door_off); - sc->sc_event_ccs = 1; sc->sc_event_idx = 0; sc->sc_command_ccs = 1; sc->sc_command_idx = 0; - DPRINTF("xHCI version = 0x%04x\n", XREAD2(sc, capa, XHCI_HCIVERSION)); - - temp = XREAD4(sc, capa, XHCI_HCSPARAMS0); - - DPRINTF("HCS0 = 0x%08x\n", temp); - - if (XHCI_HCS0_CSZ(temp)) { - sc->sc_ctx_is_64_byte = 1; - device_printf(sc->sc_bus.parent, "64 byte context size.\n"); - } else { - sc->sc_ctx_is_64_byte = 0; - device_printf(sc->sc_bus.parent, "32 byte context size.\n"); - } - /* Reset controller */ XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_HCRST); for (i = 0; i != 100; i++) { usb_pause_mtx(NULL, hz / 100); temp = (XREAD4(sc, oper, XHCI_USBCMD) & XHCI_CMD_HCRST) | (XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_CNR); if (!temp) break; } if (temp) { device_printf(sc->sc_bus.parent, "Controller " "reset timeout.\n"); return (USB_ERR_IOERROR); } if (!(XREAD4(sc, oper, XHCI_PAGESIZE) & XHCI_PAGESIZE_4K)) { device_printf(sc->sc_bus.parent, "Controller does " "not support 4K page size.\n"); return (USB_ERR_IOERROR); } temp = XREAD4(sc, capa, XHCI_HCSPARAMS1); i = XHCI_HCS1_N_PORTS(temp); if (i == 0) { device_printf(sc->sc_bus.parent, "Invalid number " "of ports: %u\n", i); return (USB_ERR_IOERROR); } sc->sc_noport = i; sc->sc_noslot = XHCI_HCS1_DEVSLOT_MAX(temp); if (sc->sc_noslot > XHCI_MAX_DEVICES) sc->sc_noslot = XHCI_MAX_DEVICES; - /* setup number of device slots */ + /* set up number of device slots */ DPRINTF("CONFIG=0x%08x -> 0x%08x\n", XREAD4(sc, oper, XHCI_CONFIG), sc->sc_noslot); XWRITE4(sc, oper, XHCI_CONFIG, sc->sc_noslot); DPRINTF("Max slots: %u\n", sc->sc_noslot); temp = XREAD4(sc, capa, XHCI_HCSPARAMS2); sc->sc_noscratch = XHCI_HCS2_SPB_MAX(temp); if (sc->sc_noscratch > XHCI_MAX_SCRATCHPADS) { device_printf(sc->sc_bus.parent, "XHCI request " "too many scratchpads\n"); return (USB_ERR_NOMEM); } DPRINTF("Max scratch: %u\n", sc->sc_noscratch); temp = XREAD4(sc, capa, XHCI_HCSPARAMS3); sc->sc_exit_lat_max = XHCI_HCS3_U1_DEL(temp) + XHCI_HCS3_U2_DEL(temp) + 250 /* us */; temp = XREAD4(sc, oper, XHCI_USBSTS); /* clear interrupts */ XWRITE4(sc, oper, XHCI_USBSTS, temp); /* disable all device notifications */ XWRITE4(sc, oper, XHCI_DNCTRL, 0); - /* setup device context base address */ + /* set up device context base address */ usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res); pdctxa = buf_res.buffer; memset(pdctxa, 0, sizeof(*pdctxa)); addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_dev_ctx_addr *)0)->qwSpBufPtr[0]; /* slot 0 points to the table of scratchpad pointers */ pdctxa->qwBaaDevCtxAddr[0] = htole64(addr); for (i = 0; i != sc->sc_noscratch; i++) { struct usb_page_search buf_scp; usbd_get_page(&sc->sc_hw.scratch_pc[i], 0, &buf_scp); pdctxa->qwSpBufPtr[i] = htole64((uint64_t)buf_scp.physaddr); } addr = buf_res.physaddr; XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr); XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32)); XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr); XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32)); /* Setup event table size */ temp = XREAD4(sc, capa, XHCI_HCSPARAMS2); DPRINTF("HCS2=0x%08x\n", temp); temp = XHCI_HCS2_ERST_MAX(temp); temp = 1U << temp; if (temp > XHCI_MAX_RSEG) temp = XHCI_MAX_RSEG; sc->sc_erst_max = temp; DPRINTF("ERSTSZ=0x%08x -> 0x%08x\n", XREAD4(sc, runt, XHCI_ERSTSZ(0)), temp); XWRITE4(sc, runt, XHCI_ERSTSZ(0), XHCI_ERSTS_SET(temp)); /* Check if we should use the default IMOD value */ if (sc->sc_imod_default == 0) sc->sc_imod_default = XHCI_IMOD_DEFAULT; /* Setup interrupt rate */ XWRITE4(sc, runt, XHCI_IMOD(0), sc->sc_imod_default); usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); phwr = buf_res.buffer; addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[0]; /* reset hardware root structure */ memset(phwr, 0, sizeof(*phwr)); phwr->hwr_ring_seg[0].qwEvrsTablePtr = htole64(addr); phwr->hwr_ring_seg[0].dwEvrsTableSize = htole32(XHCI_MAX_EVENTS); DPRINTF("ERDP(0)=0x%016llx\n", (unsigned long long)addr); XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr); XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32)); addr = (uint64_t)buf_res.physaddr; DPRINTF("ERSTBA(0)=0x%016llx\n", (unsigned long long)addr); XWRITE4(sc, runt, XHCI_ERSTBA_LO(0), (uint32_t)addr); XWRITE4(sc, runt, XHCI_ERSTBA_HI(0), (uint32_t)(addr >> 32)); /* Setup interrupter registers */ temp = XREAD4(sc, runt, XHCI_IMAN(0)); temp |= XHCI_IMAN_INTR_ENA; XWRITE4(sc, runt, XHCI_IMAN(0), temp); - /* setup command ring control base address */ + /* set up command ring control base address */ addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0]; DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr); XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS); XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32)); phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr); usb_bus_mem_flush_all(&sc->sc_bus, &xhci_iterate_hw_softc); /* Go! */ XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_RS | XHCI_CMD_INTE | XHCI_CMD_HSEE); for (i = 0; i != 100; i++) { usb_pause_mtx(NULL, hz / 100); temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH; if (!temp) break; } if (temp) { XWRITE4(sc, oper, XHCI_USBCMD, 0); device_printf(sc->sc_bus.parent, "Run timeout.\n"); return (USB_ERR_IOERROR); } /* catch any lost interrupts */ xhci_do_poll(&sc->sc_bus); if (sc->sc_port_route != NULL) { /* Route all ports to the XHCI by default */ sc->sc_port_route(sc->sc_bus.parent, ~xhciroute, xhciroute); } return (0); } usb_error_t xhci_halt_controller(struct xhci_softc *sc) { uint32_t temp; uint16_t i; DPRINTF("\n"); sc->sc_capa_off = 0; sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH); sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0xF; sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3; /* Halt controller */ XWRITE4(sc, oper, XHCI_USBCMD, 0); for (i = 0; i != 100; i++) { usb_pause_mtx(NULL, hz / 100); temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH; if (temp) break; } if (!temp) { device_printf(sc->sc_bus.parent, "Controller halt timeout.\n"); return (USB_ERR_IOERROR); } return (0); } usb_error_t xhci_init(struct xhci_softc *sc, device_t self) { - /* initialise some bus fields */ + uint32_t temp; + + DPRINTF("\n"); + + /* initialize some bus fields */ sc->sc_bus.parent = self; /* set the bus revision */ sc->sc_bus.usbrev = USB_REV_3_0; /* set up the bus struct */ sc->sc_bus.methods = &xhci_bus_methods; - /* setup devices array */ + /* set up devices array */ sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = XHCI_MAX_DEVICES; /* set default cycle state in case of early interrupts */ sc->sc_event_ccs = 1; sc->sc_command_ccs = 1; - /* setup command queue mutex and condition varible */ - cv_init(&sc->sc_cmd_cv, "CMDQ"); - sx_init(&sc->sc_cmd_sx, "CMDQ lock"); + /* set up bus space offsets */ + sc->sc_capa_off = 0; + sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH); + sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F; + sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3; + DPRINTF("CAPLENGTH=0x%x\n", sc->sc_oper_off); + DPRINTF("RUNTIMEOFFSET=0x%x\n", sc->sc_runt_off); + DPRINTF("DOOROFFSET=0x%x\n", sc->sc_door_off); + + DPRINTF("xHCI version = 0x%04x\n", XREAD2(sc, capa, XHCI_HCIVERSION)); + + temp = XREAD4(sc, capa, XHCI_HCSPARAMS0); + + DPRINTF("HCS0 = 0x%08x\n", temp); + + /* set up context size */ + if (XHCI_HCS0_CSZ(temp)) { + sc->sc_ctx_is_64_byte = 1; + } else { + sc->sc_ctx_is_64_byte = 0; + } + + /* get DMA bits */ + sc->sc_bus.dma_bits = XHCI_HCS0_AC64(temp) ? 64 : 32; + + device_printf(self, "%d bytes context size, %d-bit DMA\n", + sc->sc_ctx_is_64_byte ? 64 : 32, (int)sc->sc_bus.dma_bits); + /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &xhci_iterate_hw_softc)) { return (ENOMEM); } - sc->sc_config_msg[0].hdr.pm_callback = &xhci_configure_msg; - sc->sc_config_msg[0].bus = &sc->sc_bus; - sc->sc_config_msg[1].hdr.pm_callback = &xhci_configure_msg; - sc->sc_config_msg[1].bus = &sc->sc_bus; + /* set up command queue mutex and condition varible */ + cv_init(&sc->sc_cmd_cv, "CMDQ"); + sx_init(&sc->sc_cmd_sx, "CMDQ lock"); + sc->sc_config_msg[0].hdr.pm_callback = &xhci_configure_msg; + sc->sc_config_msg[0].bus = &sc->sc_bus; + sc->sc_config_msg[1].hdr.pm_callback = &xhci_configure_msg; + sc->sc_config_msg[1].bus = &sc->sc_bus; + return (0); } void xhci_uninit(struct xhci_softc *sc) { /* * NOTE: At this point the control transfer process is gone * and "xhci_configure_msg" is no longer called. Consequently * waiting for the configuration messages to complete is not * needed. */ usb_bus_mem_free_all(&sc->sc_bus, &xhci_iterate_hw_softc); cv_destroy(&sc->sc_cmd_cv); sx_destroy(&sc->sc_cmd_sx); } static void xhci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) { struct xhci_softc *sc = XHCI_BUS2SC(bus); switch (state) { case USB_HW_POWER_SUSPEND: DPRINTF("Stopping the XHCI\n"); xhci_halt_controller(sc); break; case USB_HW_POWER_SHUTDOWN: DPRINTF("Stopping the XHCI\n"); xhci_halt_controller(sc); break; case USB_HW_POWER_RESUME: DPRINTF("Starting the XHCI\n"); xhci_start_controller(sc); break; default: break; } } static usb_error_t xhci_generic_done_sub(struct usb_xfer *xfer) { struct xhci_td *td; struct xhci_td *td_alt_next; uint32_t len; uint8_t status; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; if (xfer->aframes != xfer->nframes) usbd_xfer_set_frame_len(xfer, xfer->aframes, 0); while (1) { usb_pc_cpu_invalidate(td->page_cache); status = td->status; len = td->remainder; DPRINTFN(4, "xfer=%p[%u/%u] rem=%u/%u status=%u\n", xfer, (unsigned int)xfer->aframes, (unsigned int)xfer->nframes, (unsigned int)len, (unsigned int)td->len, (unsigned int)status); /* * Verify the status length and * add the length to "frlengths[]": */ if (len > td->len) { /* should not happen */ DPRINTF("Invalid status length, " "0x%04x/0x%04x bytes\n", len, td->len); status = XHCI_TRB_ERROR_LENGTH; } else if (xfer->aframes != xfer->nframes) { xfer->frlengths[xfer->aframes] += td->len - len; } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; break; } /* Check for transfer error */ if (status != XHCI_TRB_ERROR_SHORT_PKT && status != XHCI_TRB_ERROR_SUCCESS) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr || xfer->flags_int.control_xfr) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; return ((status == XHCI_TRB_ERROR_STALL) ? USB_ERR_STALLED : (status != XHCI_TRB_ERROR_SHORT_PKT && status != XHCI_TRB_ERROR_SUCCESS) ? USB_ERR_IOERROR : USB_ERR_NORMAL_COMPLETION); } static void xhci_generic_done(struct usb_xfer *xfer) { usb_error_t err = 0; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) err = xhci_generic_done_sub(xfer); xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) goto done; } while (xfer->aframes != xfer->nframes) { err = xhci_generic_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) goto done; } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) err = xhci_generic_done_sub(xfer); done: /* transfer is complete */ xhci_device_done(xfer, err); } static void xhci_activate_transfer(struct usb_xfer *xfer) { struct xhci_td *td; td = xfer->td_transfer_cache; usb_pc_cpu_invalidate(td->page_cache); if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) { /* activate the transfer */ td->td_trb[0].dwTrb3 |= htole32(XHCI_TRB_3_CYCLE_BIT); usb_pc_cpu_flush(td->page_cache); xhci_endpoint_doorbell(xfer); } } static void xhci_skip_transfer(struct usb_xfer *xfer) { struct xhci_td *td; struct xhci_td *td_last; td = xfer->td_transfer_cache; td_last = xfer->td_transfer_last; td = td->alt_next; usb_pc_cpu_invalidate(td->page_cache); if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) { usb_pc_cpu_invalidate(td_last->page_cache); /* copy LINK TRB to current waiting location */ td->td_trb[0].qwTrb0 = td_last->td_trb[td_last->ntrb].qwTrb0; td->td_trb[0].dwTrb2 = td_last->td_trb[td_last->ntrb].dwTrb2; usb_pc_cpu_flush(td->page_cache); td->td_trb[0].dwTrb3 = td_last->td_trb[td_last->ntrb].dwTrb3; usb_pc_cpu_flush(td->page_cache); xhci_endpoint_doorbell(xfer); } } /*------------------------------------------------------------------------* * xhci_check_transfer *------------------------------------------------------------------------*/ static void xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb) { struct xhci_endpoint_ext *pepext; int64_t offset; uint64_t td_event; uint32_t temp; uint32_t remainder; uint16_t stream_id; uint16_t i; uint8_t status; uint8_t halted; uint8_t epno; uint8_t index; /* decode TRB */ td_event = le64toh(trb->qwTrb0); temp = le32toh(trb->dwTrb2); remainder = XHCI_TRB_2_REM_GET(temp); status = XHCI_TRB_2_ERROR_GET(temp); stream_id = XHCI_TRB_2_STREAM_GET(temp); temp = le32toh(trb->dwTrb3); epno = XHCI_TRB_3_EP_GET(temp); index = XHCI_TRB_3_SLOT_GET(temp); /* check if error means halted */ halted = (status != XHCI_TRB_ERROR_SHORT_PKT && status != XHCI_TRB_ERROR_SUCCESS); DPRINTF("slot=%u epno=%u stream=%u remainder=%u status=%u\n", index, epno, stream_id, remainder, status); if (index > sc->sc_noslot) { DPRINTF("Invalid slot.\n"); return; } if ((epno == 0) || (epno >= XHCI_MAX_ENDPOINTS)) { DPRINTF("Invalid endpoint.\n"); return; } pepext = &sc->sc_hw.devs[index].endp[epno]; if (pepext->trb_ep_mode != USB_EP_MODE_STREAMS) { stream_id = 0; DPRINTF("stream_id=0\n"); } else if (stream_id >= XHCI_MAX_STREAMS) { DPRINTF("Invalid stream ID.\n"); return; } /* try to find the USB transfer that generated the event */ for (i = 0; i != (XHCI_MAX_TRANSFERS - 1); i++) { struct usb_xfer *xfer; struct xhci_td *td; xfer = pepext->xfer[i + (XHCI_MAX_TRANSFERS * stream_id)]; if (xfer == NULL) continue; td = xfer->td_transfer_cache; DPRINTFN(5, "Checking if 0x%016llx == (0x%016llx .. 0x%016llx)\n", (long long)td_event, (long long)td->td_self, (long long)td->td_self + sizeof(td->td_trb)); /* * NOTE: Some XHCI implementations might not trigger * an event on the last LINK TRB so we need to * consider both the last and second last event * address as conditions for a successful transfer. * * NOTE: We assume that the XHCI will only trigger one * event per chain of TRBs. */ offset = td_event - td->td_self; if (offset >= 0 && offset < (int64_t)sizeof(td->td_trb)) { usb_pc_cpu_invalidate(td->page_cache); /* compute rest of remainder, if any */ for (i = (offset / 16) + 1; i < td->ntrb; i++) { temp = le32toh(td->td_trb[i].dwTrb2); remainder += XHCI_TRB_2_BYTES_GET(temp); } DPRINTFN(5, "New remainder: %u\n", remainder); /* clear isochronous transfer errors */ if (xfer->flags_int.isochronous_xfr) { if (halted) { halted = 0; status = XHCI_TRB_ERROR_SUCCESS; remainder = td->len; } } /* "td->remainder" is verified later */ td->remainder = remainder; td->status = status; usb_pc_cpu_flush(td->page_cache); /* * 1) Last transfer descriptor makes the * transfer done */ if (((void *)td) == xfer->td_transfer_last) { DPRINTF("TD is last\n"); xhci_generic_done(xfer); break; } /* * 2) Any kind of error makes the transfer * done */ if (halted) { DPRINTF("TD has I/O error\n"); xhci_generic_done(xfer); break; } /* * 3) If there is no alternate next transfer, * a short packet also makes the transfer done */ if (td->remainder > 0) { if (td->alt_next == NULL) { DPRINTF( "short TD has no alternate next\n"); xhci_generic_done(xfer); break; } DPRINTF("TD has short pkt\n"); if (xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr || xfer->flags_int.control_xfr) { /* follow the alt next */ xfer->td_transfer_cache = td->alt_next; xhci_activate_transfer(xfer); break; } xhci_skip_transfer(xfer); xhci_generic_done(xfer); break; } /* * 4) Transfer complete - go to next TD */ DPRINTF("Following next TD\n"); xfer->td_transfer_cache = td->obj_next; xhci_activate_transfer(xfer); break; /* there should only be one match */ } } } static int xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb) { if (sc->sc_cmd_addr == trb->qwTrb0) { DPRINTF("Received command event\n"); sc->sc_cmd_result[0] = trb->dwTrb2; sc->sc_cmd_result[1] = trb->dwTrb3; cv_signal(&sc->sc_cmd_cv); return (1); /* command match */ } return (0); } static int xhci_interrupt_poll(struct xhci_softc *sc) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; uint64_t addr; uint32_t temp; int retval = 0; uint16_t i; uint8_t event; uint8_t j; uint8_t k; uint8_t t; usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); phwr = buf_res.buffer; /* Receive any events */ usb_pc_cpu_invalidate(&sc->sc_hw.root_pc); i = sc->sc_event_idx; j = sc->sc_event_ccs; t = 2; while (1) { temp = le32toh(phwr->hwr_events[i].dwTrb3); k = (temp & XHCI_TRB_3_CYCLE_BIT) ? 1 : 0; if (j != k) break; event = XHCI_TRB_3_TYPE_GET(temp); DPRINTFN(10, "event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n", i, event, (long long)le64toh(phwr->hwr_events[i].qwTrb0), (long)le32toh(phwr->hwr_events[i].dwTrb2), (long)le32toh(phwr->hwr_events[i].dwTrb3)); switch (event) { case XHCI_TRB_EVENT_TRANSFER: xhci_check_transfer(sc, &phwr->hwr_events[i]); break; case XHCI_TRB_EVENT_CMD_COMPLETE: retval |= xhci_check_command(sc, &phwr->hwr_events[i]); break; default: DPRINTF("Unhandled event = %u\n", event); break; } i++; if (i == XHCI_MAX_EVENTS) { i = 0; j ^= 1; /* check for timeout */ if (!--t) break; } } sc->sc_event_idx = i; sc->sc_event_ccs = j; /* * NOTE: The Event Ring Dequeue Pointer Register is 64-bit * latched. That means to activate the register we need to * write both the low and high double word of the 64-bit * register. */ addr = (uint32_t)buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[i]; /* try to clear busy bit */ addr |= XHCI_ERDP_LO_BUSY; XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr); XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32)); return (retval); } static usb_error_t xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb, uint16_t timeout_ms) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; uint64_t addr; uint32_t temp; uint8_t i; uint8_t j; uint8_t timeout = 0; int err; XHCI_CMD_ASSERT_LOCKED(sc); /* get hardware root structure */ usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); phwr = buf_res.buffer; /* Queue command */ USB_BUS_LOCK(&sc->sc_bus); retry: i = sc->sc_command_idx; j = sc->sc_command_ccs; DPRINTFN(10, "command[%u] = %u (0x%016llx, 0x%08lx, 0x%08lx)\n", i, XHCI_TRB_3_TYPE_GET(le32toh(trb->dwTrb3)), (long long)le64toh(trb->qwTrb0), (long)le32toh(trb->dwTrb2), (long)le32toh(trb->dwTrb3)); phwr->hwr_commands[i].qwTrb0 = trb->qwTrb0; phwr->hwr_commands[i].dwTrb2 = trb->dwTrb2; usb_pc_cpu_flush(&sc->sc_hw.root_pc); temp = trb->dwTrb3; if (j) temp |= htole32(XHCI_TRB_3_CYCLE_BIT); else temp &= ~htole32(XHCI_TRB_3_CYCLE_BIT); temp &= ~htole32(XHCI_TRB_3_TC_BIT); phwr->hwr_commands[i].dwTrb3 = temp; usb_pc_cpu_flush(&sc->sc_hw.root_pc); addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[i]; sc->sc_cmd_addr = htole64(addr); i++; if (i == (XHCI_MAX_COMMANDS - 1)) { if (j) { temp = htole32(XHCI_TRB_3_TC_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | XHCI_TRB_3_CYCLE_BIT); } else { temp = htole32(XHCI_TRB_3_TC_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); } phwr->hwr_commands[i].dwTrb3 = temp; usb_pc_cpu_flush(&sc->sc_hw.root_pc); i = 0; j ^= 1; } sc->sc_command_idx = i; sc->sc_command_ccs = j; XWRITE4(sc, door, XHCI_DOORBELL(0), 0); err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(timeout_ms)); /* * In some error cases event interrupts are not generated. * Poll one time to see if the command has completed. */ if (err != 0 && xhci_interrupt_poll(sc) != 0) { DPRINTF("Command was completed when polling\n"); err = 0; } if (err != 0) { DPRINTF("Command timeout!\n"); /* * After some weeks of continuous operation, it has * been observed that the ASMedia Technology, ASM1042 * SuperSpeed USB Host Controller can suddenly stop * accepting commands via the command queue. Try to * first reset the command queue. If that fails do a * host controller reset. */ if (timeout == 0 && xhci_reset_command_queue_locked(sc) == 0) { temp = le32toh(trb->dwTrb3); /* * Avoid infinite XHCI reset loops if the set * address command fails to respond due to a * non-enumerating device: */ if (XHCI_TRB_3_TYPE_GET(temp) == XHCI_TRB_TYPE_ADDRESS_DEVICE && (temp & XHCI_TRB_3_BSR_BIT) == 0) { DPRINTF("Set address timeout\n"); } else { timeout = 1; goto retry; } } else { DPRINTF("Controller reset!\n"); usb_bus_reset_async_locked(&sc->sc_bus); } err = USB_ERR_TIMEOUT; trb->dwTrb2 = 0; trb->dwTrb3 = 0; } else { temp = le32toh(sc->sc_cmd_result[0]); if (XHCI_TRB_2_ERROR_GET(temp) != XHCI_TRB_ERROR_SUCCESS) err = USB_ERR_IOERROR; trb->dwTrb2 = sc->sc_cmd_result[0]; trb->dwTrb3 = sc->sc_cmd_result[1]; } USB_BUS_UNLOCK(&sc->sc_bus); return (err); } #if 0 static usb_error_t xhci_cmd_nop(struct xhci_softc *sc) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NOOP); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } #endif static usb_error_t xhci_cmd_enable_slot(struct xhci_softc *sc, uint8_t *pslot) { struct xhci_trb trb; uint32_t temp; usb_error_t err; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; trb.dwTrb3 = htole32(XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ENABLE_SLOT)); err = xhci_do_command(sc, &trb, 100 /* ms */); if (err) goto done; temp = le32toh(trb.dwTrb3); *pslot = XHCI_TRB_3_SLOT_GET(temp); done: return (err); } static usb_error_t xhci_cmd_disable_slot(struct xhci_softc *sc, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DISABLE_SLOT) | XHCI_TRB_3_SLOT_SET(slot_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_set_address(struct xhci_softc *sc, uint64_t input_ctx, uint8_t bsr, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(input_ctx); trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ADDRESS_DEVICE) | XHCI_TRB_3_SLOT_SET(slot_id); if (bsr) temp |= XHCI_TRB_3_BSR_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 500 /* ms */)); } static usb_error_t xhci_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t address) { struct usb_page_search buf_inp; struct usb_page_search buf_dev; struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct xhci_hw_dev *hdev; struct xhci_dev_ctx *pdev; struct xhci_endpoint_ext *pepext; uint32_t temp; uint16_t mps; usb_error_t err; uint8_t index; /* the root HUB case is not handled here */ if (udev->parent_hub == NULL) return (USB_ERR_INVAL); index = udev->controller_slot_id; hdev = &sc->sc_hw.devs[index]; if (mtx != NULL) mtx_unlock(mtx); XHCI_CMD_LOCK(sc); switch (hdev->state) { case XHCI_ST_DEFAULT: case XHCI_ST_ENABLED: hdev->state = XHCI_ST_ENABLED; /* set configure mask to slot and EP0 */ xhci_configure_mask(udev, 3, 0); /* configure input slot context structure */ err = xhci_configure_device(udev); if (err != 0) { DPRINTF("Could not configure device\n"); break; } /* configure input endpoint context structure */ switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: mps = 8; break; case USB_SPEED_HIGH: mps = 64; break; default: mps = 512; break; } pepext = xhci_get_endpoint_ext(udev, &udev->ctrl_ep_desc); err = xhci_configure_endpoint(udev, &udev->ctrl_ep_desc, pepext, 0, 1, 1, 0, mps, mps, USB_EP_MODE_DEFAULT); if (err != 0) { DPRINTF("Could not configure default endpoint\n"); break; } /* execute set address command */ usbd_get_page(&hdev->input_pc, 0, &buf_inp); err = xhci_cmd_set_address(sc, buf_inp.physaddr, (address == 0), index); if (err != 0) { temp = le32toh(sc->sc_cmd_result[0]); if (address == 0 && sc->sc_port_route != NULL && XHCI_TRB_2_ERROR_GET(temp) == XHCI_TRB_ERROR_PARAMETER) { /* LynxPoint XHCI - ports are not switchable */ /* Un-route all ports from the XHCI */ sc->sc_port_route(sc->sc_bus.parent, 0, ~0); } DPRINTF("Could not set address " "for slot %u.\n", index); if (address != 0) break; } /* update device address to new value */ usbd_get_page(&hdev->device_pc, 0, &buf_dev); pdev = buf_dev.buffer; usb_pc_cpu_invalidate(&hdev->device_pc); temp = xhci_ctx_get_le32(sc, &pdev->ctx_slot.dwSctx3); udev->address = XHCI_SCTX_3_DEV_ADDR_GET(temp); /* update device state to new value */ if (address != 0) hdev->state = XHCI_ST_ADDRESSED; else hdev->state = XHCI_ST_DEFAULT; break; default: DPRINTF("Wrong state for set address.\n"); err = USB_ERR_IOERROR; break; } XHCI_CMD_UNLOCK(sc); if (mtx != NULL) mtx_lock(mtx); return (err); } static usb_error_t xhci_cmd_configure_ep(struct xhci_softc *sc, uint64_t input_ctx, uint8_t deconfigure, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(input_ctx); trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP) | XHCI_TRB_3_SLOT_SET(slot_id); if (deconfigure) temp |= XHCI_TRB_3_DCEP_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_evaluate_ctx(struct xhci_softc *sc, uint64_t input_ctx, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(input_ctx); trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVALUATE_CTX) | XHCI_TRB_3_SLOT_SET(slot_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_reset_ep(struct xhci_softc *sc, uint8_t preserve, uint8_t ep_id, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP) | XHCI_TRB_3_SLOT_SET(slot_id) | XHCI_TRB_3_EP_SET(ep_id); if (preserve) temp |= XHCI_TRB_3_PRSV_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_set_tr_dequeue_ptr(struct xhci_softc *sc, uint64_t dequeue_ptr, uint16_t stream_id, uint8_t ep_id, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(dequeue_ptr); temp = XHCI_TRB_2_STREAM_SET(stream_id); trb.dwTrb2 = htole32(temp); temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE) | XHCI_TRB_3_SLOT_SET(slot_id) | XHCI_TRB_3_EP_SET(ep_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_stop_ep(struct xhci_softc *sc, uint8_t suspend, uint8_t ep_id, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP) | XHCI_TRB_3_SLOT_SET(slot_id) | XHCI_TRB_3_EP_SET(ep_id); if (suspend) temp |= XHCI_TRB_3_SUSP_EP_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_reset_dev(struct xhci_softc *sc, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_DEVICE) | XHCI_TRB_3_SLOT_SET(slot_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } /*------------------------------------------------------------------------* * xhci_interrupt - XHCI interrupt handler *------------------------------------------------------------------------*/ void xhci_interrupt(struct xhci_softc *sc) { uint32_t status; uint32_t temp; USB_BUS_LOCK(&sc->sc_bus); status = XREAD4(sc, oper, XHCI_USBSTS); /* acknowledge interrupts, if any */ if (status != 0) { XWRITE4(sc, oper, XHCI_USBSTS, status); DPRINTFN(16, "real interrupt (status=0x%08x)\n", status); } temp = XREAD4(sc, runt, XHCI_IMAN(0)); /* force clearing of pending interrupts */ if (temp & XHCI_IMAN_INTR_PEND) XWRITE4(sc, runt, XHCI_IMAN(0), temp); /* check for event(s) */ xhci_interrupt_poll(sc); if (status & (XHCI_STS_PCD | XHCI_STS_HCH | XHCI_STS_HSE | XHCI_STS_HCE)) { if (status & XHCI_STS_PCD) { xhci_root_intr(sc); } if (status & XHCI_STS_HCH) { printf("%s: host controller halted\n", __FUNCTION__); } if (status & XHCI_STS_HSE) { printf("%s: host system error\n", __FUNCTION__); } if (status & XHCI_STS_HCE) { printf("%s: host controller error\n", __FUNCTION__); } } USB_BUS_UNLOCK(&sc->sc_bus); } /*------------------------------------------------------------------------* * xhci_timeout - XHCI timeout handler *------------------------------------------------------------------------*/ static void xhci_timeout(void *arg) { struct usb_xfer *xfer = arg; DPRINTF("xfer=%p\n", xfer); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* transfer is transferred */ xhci_device_done(xfer, USB_ERR_TIMEOUT); } static void xhci_do_poll(struct usb_bus *bus) { struct xhci_softc *sc = XHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); xhci_interrupt_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void xhci_setup_generic_chain_sub(struct xhci_std_temp *temp) { struct usb_page_search buf_res; struct xhci_td *td; struct xhci_td *td_next; struct xhci_td *td_alt_next; struct xhci_td *td_first; uint32_t buf_offset; uint32_t average; uint32_t len_old; uint32_t npkt_off; uint32_t dword; uint8_t shortpkt_old; uint8_t precompute; uint8_t x; td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; len_old = temp->len; npkt_off = 0; precompute = 1; restart: td = temp->td; td_next = td_first = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) break; /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; average = 0; } else { average = temp->average; if (temp->len < average) { if (temp->len % temp->max_packet_size) { temp->shortpkt = 1; } average = temp->len; } } if (td_next == NULL) panic("%s: out of XHCI transfer descriptors!", __FUNCTION__); /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->len = average; td->remainder = 0; td->status = 0; /* update remaining length */ temp->len -= average; /* reset TRB index */ x = 0; if (temp->trb_type == XHCI_TRB_TYPE_SETUP_STAGE) { /* immediate data */ if (average > 8) average = 8; td->td_trb[0].qwTrb0 = 0; usbd_copy_out(temp->pc, temp->offset + buf_offset, (uint8_t *)(uintptr_t)&td->td_trb[0].qwTrb0, average); dword = XHCI_TRB_2_BYTES_SET(8) | XHCI_TRB_2_TDSZ_SET(0) | XHCI_TRB_2_IRQ_SET(0); td->td_trb[0].dwTrb2 = htole32(dword); dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) | XHCI_TRB_3_IDT_BIT | XHCI_TRB_3_CYCLE_BIT; /* check wLength */ if (td->td_trb[0].qwTrb0 & htole64(XHCI_TRB_0_WLENGTH_MASK)) { if (td->td_trb[0].qwTrb0 & htole64(XHCI_TRB_0_DIR_IN_MASK)) dword |= XHCI_TRB_3_TRT_IN; else dword |= XHCI_TRB_3_TRT_OUT; } td->td_trb[0].dwTrb3 = htole32(dword); #ifdef USB_DEBUG xhci_dump_trb(&td->td_trb[x]); #endif x++; } else do { uint32_t npkt; /* fill out buffer pointers */ if (average == 0) { memset(&buf_res, 0, sizeof(buf_res)); } else { usbd_get_page(temp->pc, temp->offset + buf_offset, &buf_res); /* get length to end of page */ if (buf_res.length > average) buf_res.length = average; /* check for maximum length */ if (buf_res.length > XHCI_TD_PAGE_SIZE) buf_res.length = XHCI_TD_PAGE_SIZE; npkt_off += buf_res.length; } - /* setup npkt */ + /* set up npkt */ npkt = (len_old - npkt_off + temp->max_packet_size - 1) / temp->max_packet_size; if (npkt == 0) npkt = 1; else if (npkt > 31) npkt = 31; /* fill out TRB's */ td->td_trb[x].qwTrb0 = htole64((uint64_t)buf_res.physaddr); dword = XHCI_TRB_2_BYTES_SET(buf_res.length) | XHCI_TRB_2_TDSZ_SET(npkt) | XHCI_TRB_2_IRQ_SET(0); td->td_trb[x].dwTrb2 = htole32(dword); switch (temp->trb_type) { case XHCI_TRB_TYPE_ISOCH: dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TBC_SET(temp->tbc) | XHCI_TRB_3_TLBPC_SET(temp->tlbpc); if (td != td_first) { dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL); } else if (temp->do_isoc_sync != 0) { temp->do_isoc_sync = 0; /* wait until "isoc_frame" */ dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ISOCH) | XHCI_TRB_3_FRID_SET(temp->isoc_frame / 8); } else { /* start data transfer at next interval */ dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ISOCH) | XHCI_TRB_3_ISO_SIA_BIT; } if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_ISP_BIT; break; case XHCI_TRB_TYPE_DATA_STAGE: dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE); if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_DIR_IN | XHCI_TRB_3_ISP_BIT; break; case XHCI_TRB_TYPE_STATUS_STAGE: dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE); if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_DIR_IN; break; default: /* XHCI_TRB_TYPE_NORMAL */ dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL); if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_ISP_BIT; break; } td->td_trb[x].dwTrb3 = htole32(dword); average -= buf_res.length; buf_offset += buf_res.length; #ifdef USB_DEBUG xhci_dump_trb(&td->td_trb[x]); #endif x++; } while (average != 0); td->td_trb[x-1].dwTrb3 |= htole32(XHCI_TRB_3_IOC_BIT); /* store number of data TRB's */ td->ntrb = x; DPRINTF("NTRB=%u\n", x); /* fill out link TRB */ if (td_next != NULL) { /* link the current TD with the next one */ td->td_trb[x].qwTrb0 = htole64((uint64_t)td_next->td_self); DPRINTF("LINK=0x%08llx\n", (long long)td_next->td_self); } else { /* this field will get updated later */ DPRINTF("NOLINK\n"); } dword = XHCI_TRB_2_IRQ_SET(0); td->td_trb[x].dwTrb2 = htole32(dword); dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_IOC_BIT | /* * CHAIN-BIT: Ensure that a multi-TRB IN-endpoint * frame only receives a single short packet event * by setting the CHAIN bit in the LINK field. In * addition some XHCI controllers have problems * sending a ZLP unless the CHAIN-BIT is set in * the LINK TRB. */ XHCI_TRB_3_CHAIN_BIT; td->td_trb[x].dwTrb3 = htole32(dword); td->alt_next = td_alt_next; #ifdef USB_DEBUG xhci_dump_trb(&td->td_trb[x]); #endif usb_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; - /* setup alt next pointer, if any */ + /* set up alt next pointer, if any */ if (temp->last_frame) { td_alt_next = NULL; } else { /* we use this field internally */ td_alt_next = td_next; } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } /* * Remove cycle bit from the first TRB if we are * stepping them: */ if (temp->step_td != 0) { td_first->td_trb[0].dwTrb3 &= ~htole32(XHCI_TRB_3_CYCLE_BIT); usb_pc_cpu_flush(td_first->page_cache); } /* clear TD SIZE to zero, hence this is the last TRB */ /* remove chain bit because this is the last data TRB in the chain */ td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15)); td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); /* remove CHAIN-BIT from last LINK TRB */ td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); usb_pc_cpu_flush(td->page_cache); temp->td = td; temp->td_next = td_next; } static void xhci_setup_generic_chain(struct usb_xfer *xfer) { struct xhci_std_temp temp; struct xhci_td *td; uint32_t x; uint32_t y; uint8_t mult; temp.do_isoc_sync = 0; temp.step_td = 0; temp.tbc = 0; temp.tlbpc = 0; temp.average = xfer->max_hc_frame_size; temp.max_packet_size = xfer->max_packet_size; temp.sc = XHCI_BUS2SC(xfer->xroot->bus); temp.pc = NULL; temp.last_frame = 0; temp.offset = 0; temp.multishort = xfer->flags_int.isochronous_xfr || xfer->flags_int.control_xfr || xfer->flags_int.short_frames_ok; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; temp.td = NULL; temp.td_next = td; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; if (xfer->flags_int.isochronous_xfr) { uint8_t shift; /* compute multiplier for ISOCHRONOUS transfers */ mult = xfer->endpoint->ecomp ? UE_GET_SS_ISO_MULT(xfer->endpoint->ecomp->bmAttributes) : 0; /* check for USB 2.0 multiplier */ if (mult == 0) { mult = (xfer->endpoint->edesc-> wMaxPacketSize[1] >> 3) & 3; } /* range check */ if (mult > 2) mult = 3; else mult++; x = XREAD4(temp.sc, runt, XHCI_MFINDEX); DPRINTF("MFINDEX=0x%08x\n", x); switch (usbd_get_speed(xfer->xroot->udev)) { case USB_SPEED_FULL: shift = 3; temp.isoc_delta = 8; /* 1ms */ x += temp.isoc_delta - 1; x &= ~(temp.isoc_delta - 1); break; default: shift = usbd_xfer_get_fps_shift(xfer); temp.isoc_delta = 1U << shift; x += temp.isoc_delta - 1; x &= ~(temp.isoc_delta - 1); /* simple frame load balancing */ x += xfer->endpoint->usb_uframe; break; } y = XHCI_MFINDEX_GET(x - xfer->endpoint->isoc_next); if ((xfer->endpoint->is_synced == 0) || (y < (xfer->nframes << shift)) || (XHCI_MFINDEX_GET(-y) >= (128 * 8))) { /* * If there is data underflow or the pipe * queue is empty we schedule the transfer a * few frames ahead of the current frame * position. Else two isochronous transfers * might overlap. */ xfer->endpoint->isoc_next = XHCI_MFINDEX_GET(x + (3 * 8)); xfer->endpoint->is_synced = 1; temp.do_isoc_sync = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* compute isochronous completion time */ y = XHCI_MFINDEX_GET(xfer->endpoint->isoc_next - (x & ~7)); xfer->isoc_time_complete = usb_isoc_time_expand(&temp.sc->sc_bus, x / 8) + (y / 8) + (((xfer->nframes << shift) + 7) / 8); x = 0; temp.isoc_frame = xfer->endpoint->isoc_next; temp.trb_type = XHCI_TRB_TYPE_ISOCH; xfer->endpoint->isoc_next += xfer->nframes << shift; } else if (xfer->flags_int.control_xfr) { /* check if we should prepend a setup message */ if (xfer->flags_int.control_hdr) { temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; temp.trb_type = XHCI_TRB_TYPE_SETUP_STAGE; temp.direction = 0; /* check for last frame */ if (xfer->nframes == 1) { /* no STATUS stage yet, SETUP is last */ if (xfer->flags_int.control_act) temp.last_frame = 1; } xhci_setup_generic_chain_sub(&temp); } x = 1; mult = 1; temp.isoc_delta = 0; temp.isoc_frame = 0; temp.trb_type = XHCI_TRB_TYPE_DATA_STAGE; } else { x = 0; mult = 1; temp.isoc_delta = 0; temp.isoc_frame = 0; temp.trb_type = XHCI_TRB_TYPE_NORMAL; } if (x != xfer->nframes) { - /* setup page_cache pointer */ + /* set up page_cache pointer */ temp.pc = xfer->frbuffers + x; /* set endpoint direction */ temp.direction = UE_GET_DIR(xfer->endpointno); } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.step_td = ((xfer->endpointno & UE_DIR_IN) && x != 0 && temp.multishort == 0); x++; if (x == xfer->nframes) { if (xfer->flags_int.control_xfr) { /* no STATUS stage yet, DATA is last */ if (xfer->flags_int.control_act) temp.last_frame = 1; } else { temp.last_frame = 1; } } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; temp.tbc = 0; temp.tlbpc = mult - 1; } else if (xfer->flags_int.isochronous_xfr) { uint8_t tdpc; /* * Isochronous transfers don't have short * packet termination: */ temp.shortpkt = 1; /* isochronous transfers have a transfer limit */ if (temp.len > xfer->max_frame_size) temp.len = xfer->max_frame_size; /* compute TD packet count */ tdpc = (temp.len + xfer->max_packet_size - 1) / xfer->max_packet_size; temp.tbc = ((tdpc + mult - 1) / mult) - 1; temp.tlbpc = (tdpc % mult); if (temp.tlbpc == 0) temp.tlbpc = mult - 1; else temp.tlbpc--; } else { /* regular data transfer */ temp.shortpkt = xfer->flags.force_short_xfer ? 0 : 1; } xhci_setup_generic_chain_sub(&temp); if (xfer->flags_int.isochronous_xfr) { temp.offset += xfer->frlengths[x - 1]; temp.isoc_frame += temp.isoc_delta; } else { /* get next Page Cache pointer */ temp.pc = xfer->frbuffers + x; } } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current * endpoint direction. */ temp.step_td = (xfer->nframes != 0); temp.direction = UE_GET_DIR(xfer->endpointno) ^ UE_DIR_IN; temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; temp.last_frame = 1; temp.trb_type = XHCI_TRB_TYPE_STATUS_STAGE; xhci_setup_generic_chain_sub(&temp); } td = temp.td; /* must have at least one frame! */ xfer->td_transfer_last = td; DPRINTF("first=%p last=%p\n", xfer->td_transfer_first, td); } static void xhci_set_slot_pointer(struct xhci_softc *sc, uint8_t index, uint64_t dev_addr) { struct usb_page_search buf_res; struct xhci_dev_ctx_addr *pdctxa; usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res); pdctxa = buf_res.buffer; DPRINTF("addr[%u]=0x%016llx\n", index, (long long)dev_addr); pdctxa->qwBaaDevCtxAddr[index] = htole64(dev_addr); usb_pc_cpu_flush(&sc->sc_hw.ctx_pc); } static usb_error_t xhci_configure_mask(struct usb_device *udev, uint32_t mask, uint8_t drop) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_inp; struct xhci_input_dev_ctx *pinp; uint32_t temp; uint8_t index; uint8_t x; index = udev->controller_slot_id; usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp); pinp = buf_inp.buffer; if (drop) { mask &= XHCI_INCTX_NON_CTRL_MASK; xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx0, mask); xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx1, 0); } else { /* * Some hardware requires that we drop the endpoint * context before adding it again: */ xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx0, mask & XHCI_INCTX_NON_CTRL_MASK); /* Add new endpoint context */ xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx1, mask); /* find most significant set bit */ for (x = 31; x != 1; x--) { if (mask & (1 << x)) break; } /* adjust */ x--; /* figure out the maximum number of contexts */ if (x > sc->sc_hw.devs[index].context_num) sc->sc_hw.devs[index].context_num = x; else x = sc->sc_hw.devs[index].context_num; /* update number of contexts */ temp = xhci_ctx_get_le32(sc, &pinp->ctx_slot.dwSctx0); temp &= ~XHCI_SCTX_0_CTX_NUM_SET(31); temp |= XHCI_SCTX_0_CTX_NUM_SET(x + 1); xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx0, temp); } usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc); return (0); } static usb_error_t xhci_configure_endpoint(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct xhci_endpoint_ext *pepext, uint16_t interval, uint8_t max_packet_count, uint8_t mult, uint8_t fps_shift, uint16_t max_packet_size, uint16_t max_frame_size, uint8_t ep_mode) { struct usb_page_search buf_inp; struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct xhci_input_dev_ctx *pinp; uint64_t ring_addr = pepext->physaddr; uint32_t temp; uint8_t index; uint8_t epno; uint8_t type; index = udev->controller_slot_id; usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp); pinp = buf_inp.buffer; epno = edesc->bEndpointAddress; type = edesc->bmAttributes & UE_XFERTYPE; if (type == UE_CONTROL) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); if (epno == 0) return (USB_ERR_NO_PIPE); /* invalid */ if (max_packet_count == 0) return (USB_ERR_BAD_BUFSIZE); max_packet_count--; if (mult == 0) return (USB_ERR_BAD_BUFSIZE); /* store endpoint mode */ pepext->trb_ep_mode = ep_mode; usb_pc_cpu_flush(pepext->page_cache); if (ep_mode == USB_EP_MODE_STREAMS) { temp = XHCI_EPCTX_0_EPSTATE_SET(0) | XHCI_EPCTX_0_MAXP_STREAMS_SET(XHCI_MAX_STREAMS_LOG - 1) | XHCI_EPCTX_0_LSA_SET(1); ring_addr += sizeof(struct xhci_trb) * XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS; } else { temp = XHCI_EPCTX_0_EPSTATE_SET(0) | XHCI_EPCTX_0_MAXP_STREAMS_SET(0) | XHCI_EPCTX_0_LSA_SET(0); ring_addr |= XHCI_EPCTX_2_DCS_SET(1); } switch (udev->speed) { case USB_SPEED_FULL: case USB_SPEED_LOW: /* 1ms -> 125us */ fps_shift += 3; break; default: break; } switch (type) { case UE_INTERRUPT: if (fps_shift > 3) fps_shift--; temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift); break; case UE_ISOCHRONOUS: temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift); switch (udev->speed) { case USB_SPEED_SUPER: if (mult > 3) mult = 3; temp |= XHCI_EPCTX_0_MULT_SET(mult - 1); max_packet_count /= mult; break; default: break; } break; default: break; } xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx0, temp); temp = XHCI_EPCTX_1_HID_SET(0) | XHCI_EPCTX_1_MAXB_SET(max_packet_count) | XHCI_EPCTX_1_MAXP_SIZE_SET(max_packet_size); /* * Always enable the "three strikes and you are gone" feature * except for ISOCHRONOUS endpoints. This is suggested by * section 4.3.3 in the XHCI specification about device slot * initialisation. */ if (type != UE_ISOCHRONOUS) temp |= XHCI_EPCTX_1_CERR_SET(3); switch (type) { case UE_CONTROL: temp |= XHCI_EPCTX_1_EPTYPE_SET(4); break; case UE_ISOCHRONOUS: temp |= XHCI_EPCTX_1_EPTYPE_SET(1); break; case UE_BULK: temp |= XHCI_EPCTX_1_EPTYPE_SET(2); break; default: temp |= XHCI_EPCTX_1_EPTYPE_SET(3); break; } /* check for IN direction */ if (epno & 1) temp |= XHCI_EPCTX_1_EPTYPE_SET(4); xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx1, temp); xhci_ctx_set_le64(sc, &pinp->ctx_ep[epno - 1].qwEpCtx2, ring_addr); switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_ISOCHRONOUS: temp = XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(max_frame_size) | XHCI_EPCTX_4_AVG_TRB_LEN_SET(MIN(XHCI_PAGE_SIZE, max_frame_size)); break; case UE_CONTROL: temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(8); break; default: temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(XHCI_PAGE_SIZE); break; } xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx4, temp); #ifdef USB_DEBUG xhci_dump_endpoint(sc, &pinp->ctx_ep[epno - 1]); #endif usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc); return (0); /* success */ } static usb_error_t xhci_configure_endpoint_by_xfer(struct usb_xfer *xfer) { struct xhci_endpoint_ext *pepext; struct usb_endpoint_ss_comp_descriptor *ecomp; usb_stream_t x; pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); ecomp = xfer->endpoint->ecomp; for (x = 0; x != XHCI_MAX_STREAMS; x++) { uint64_t temp; /* halt any transfers */ pepext->trb[x * XHCI_MAX_TRANSFERS].dwTrb3 = 0; /* compute start of TRB ring for stream "x" */ temp = pepext->physaddr + (x * XHCI_MAX_TRANSFERS * sizeof(struct xhci_trb)) + XHCI_SCTX_0_SCT_SEC_TR_RING; /* make tree structure */ pepext->trb[(XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS) + x].qwTrb0 = htole64(temp); /* reserved fields */ pepext->trb[(XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS) + x].dwTrb2 = 0; pepext->trb[(XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS) + x].dwTrb3 = 0; } usb_pc_cpu_flush(pepext->page_cache); return (xhci_configure_endpoint(xfer->xroot->udev, xfer->endpoint->edesc, pepext, xfer->interval, xfer->max_packet_count, (ecomp != NULL) ? UE_GET_SS_ISO_MULT(ecomp->bmAttributes) + 1 : 1, usbd_xfer_get_fps_shift(xfer), xfer->max_packet_size, xfer->max_frame_size, xfer->endpoint->ep_mode)); } static usb_error_t xhci_configure_device(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_inp; struct usb_page_cache *pcinp; struct xhci_input_dev_ctx *pinp; struct usb_device *hubdev; uint32_t temp; uint32_t route; uint32_t rh_port; uint8_t is_hub; uint8_t index; uint8_t depth; index = udev->controller_slot_id; DPRINTF("index=%u\n", index); pcinp = &sc->sc_hw.devs[index].input_pc; usbd_get_page(pcinp, 0, &buf_inp); pinp = buf_inp.buffer; rh_port = 0; route = 0; /* figure out route string and root HUB port number */ for (hubdev = udev; hubdev != NULL; hubdev = hubdev->parent_hub) { if (hubdev->parent_hub == NULL) break; depth = hubdev->parent_hub->depth; /* * NOTE: HS/FS/LS devices and the SS root HUB can have * more than 15 ports */ rh_port = hubdev->port_no; if (depth == 0) break; if (rh_port > 15) rh_port = 15; if (depth < 6) route |= rh_port << (4 * (depth - 1)); } DPRINTF("Route=0x%08x\n", route); temp = XHCI_SCTX_0_ROUTE_SET(route) | XHCI_SCTX_0_CTX_NUM_SET( sc->sc_hw.devs[index].context_num + 1); switch (udev->speed) { case USB_SPEED_LOW: temp |= XHCI_SCTX_0_SPEED_SET(2); if (udev->parent_hs_hub != NULL && udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) { DPRINTF("Device inherits MTT\n"); temp |= XHCI_SCTX_0_MTT_SET(1); } break; case USB_SPEED_HIGH: temp |= XHCI_SCTX_0_SPEED_SET(3); if (sc->sc_hw.devs[index].nports != 0 && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) { DPRINTF("HUB supports MTT\n"); temp |= XHCI_SCTX_0_MTT_SET(1); } break; case USB_SPEED_FULL: temp |= XHCI_SCTX_0_SPEED_SET(1); if (udev->parent_hs_hub != NULL && udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) { DPRINTF("Device inherits MTT\n"); temp |= XHCI_SCTX_0_MTT_SET(1); } break; default: temp |= XHCI_SCTX_0_SPEED_SET(4); break; } is_hub = sc->sc_hw.devs[index].nports != 0 && (udev->speed == USB_SPEED_SUPER || udev->speed == USB_SPEED_HIGH); if (is_hub) temp |= XHCI_SCTX_0_HUB_SET(1); xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx0, temp); temp = XHCI_SCTX_1_RH_PORT_SET(rh_port); if (is_hub) { temp |= XHCI_SCTX_1_NUM_PORTS_SET( sc->sc_hw.devs[index].nports); } switch (udev->speed) { case USB_SPEED_SUPER: switch (sc->sc_hw.devs[index].state) { case XHCI_ST_ADDRESSED: case XHCI_ST_CONFIGURED: /* enable power save */ temp |= XHCI_SCTX_1_MAX_EL_SET(sc->sc_exit_lat_max); break; default: /* disable power save */ break; } break; default: break; } xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx1, temp); temp = XHCI_SCTX_2_IRQ_TARGET_SET(0); if (is_hub) { temp |= XHCI_SCTX_2_TT_THINK_TIME_SET( sc->sc_hw.devs[index].tt); } hubdev = udev->parent_hs_hub; /* check if we should activate the transaction translator */ switch (udev->speed) { case USB_SPEED_FULL: case USB_SPEED_LOW: if (hubdev != NULL) { temp |= XHCI_SCTX_2_TT_HUB_SID_SET( hubdev->controller_slot_id); temp |= XHCI_SCTX_2_TT_PORT_NUM_SET( udev->hs_port_no); } break; default: break; } xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx2, temp); /* * These fields should be initialized to zero, according to * XHCI section 6.2.2 - slot context: */ temp = XHCI_SCTX_3_DEV_ADDR_SET(0) | XHCI_SCTX_3_SLOT_STATE_SET(0); xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx3, temp); #ifdef USB_DEBUG xhci_dump_device(sc, &pinp->ctx_slot); #endif usb_pc_cpu_flush(pcinp); return (0); /* success */ } static usb_error_t xhci_alloc_device_ext(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_dev; struct usb_page_search buf_ep; struct xhci_trb *trb; struct usb_page_cache *pc; struct usb_page *pg; uint64_t addr; uint8_t index; uint8_t i; index = udev->controller_slot_id; pc = &sc->sc_hw.devs[index].device_pc; pg = &sc->sc_hw.devs[index].device_pg; /* need to initialize the page cache */ pc->tag_parent = sc->sc_bus.dma_parent_tag; if (usb_pc_alloc_mem(pc, pg, sc->sc_ctx_is_64_byte ? (2 * sizeof(struct xhci_dev_ctx)) : sizeof(struct xhci_dev_ctx), XHCI_PAGE_SIZE)) goto error; usbd_get_page(pc, 0, &buf_dev); pc = &sc->sc_hw.devs[index].input_pc; pg = &sc->sc_hw.devs[index].input_pg; /* need to initialize the page cache */ pc->tag_parent = sc->sc_bus.dma_parent_tag; if (usb_pc_alloc_mem(pc, pg, sc->sc_ctx_is_64_byte ? (2 * sizeof(struct xhci_input_dev_ctx)) : sizeof(struct xhci_input_dev_ctx), XHCI_PAGE_SIZE)) { goto error; } - /* initialise all endpoint LINK TRBs */ + /* initialize all endpoint LINK TRBs */ for (i = 0; i != XHCI_MAX_ENDPOINTS; i++) { pc = &sc->sc_hw.devs[index].endpoint_pc[i]; pg = &sc->sc_hw.devs[index].endpoint_pg[i]; /* need to initialize the page cache */ pc->tag_parent = sc->sc_bus.dma_parent_tag; if (usb_pc_alloc_mem(pc, pg, sizeof(struct xhci_dev_endpoint_trbs), XHCI_TRB_ALIGN)) { goto error; } /* lookup endpoint TRB ring */ usbd_get_page(pc, 0, &buf_ep); /* get TRB pointer */ trb = buf_ep.buffer; trb += XHCI_MAX_TRANSFERS - 1; /* get TRB start address */ addr = buf_ep.physaddr; /* create LINK TRB */ trb->qwTrb0 = htole64(addr); trb->dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); trb->dwTrb3 = htole32(XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); usb_pc_cpu_flush(pc); } xhci_set_slot_pointer(sc, index, buf_dev.physaddr); return (0); error: xhci_free_device_ext(udev); return (USB_ERR_NOMEM); } static void xhci_free_device_ext(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; uint8_t i; index = udev->controller_slot_id; xhci_set_slot_pointer(sc, index, 0); usb_pc_free_mem(&sc->sc_hw.devs[index].device_pc); usb_pc_free_mem(&sc->sc_hw.devs[index].input_pc); for (i = 0; i != XHCI_MAX_ENDPOINTS; i++) usb_pc_free_mem(&sc->sc_hw.devs[index].endpoint_pc[i]); } static struct xhci_endpoint_ext * xhci_get_endpoint_ext(struct usb_device *udev, struct usb_endpoint_descriptor *edesc) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct xhci_endpoint_ext *pepext; struct usb_page_cache *pc; struct usb_page_search buf_ep; uint8_t epno; uint8_t index; epno = edesc->bEndpointAddress; if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); index = udev->controller_slot_id; pc = &sc->sc_hw.devs[index].endpoint_pc[epno]; usbd_get_page(pc, 0, &buf_ep); pepext = &sc->sc_hw.devs[index].endp[epno]; pepext->page_cache = pc; pepext->trb = buf_ep.buffer; pepext->physaddr = buf_ep.physaddr; return (pepext); } static void xhci_endpoint_doorbell(struct usb_xfer *xfer) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); uint8_t epno; uint8_t index; epno = xfer->endpointno; if (xfer->flags_int.control_xfr) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); index = xfer->xroot->udev->controller_slot_id; if (xfer->xroot->udev->flags.self_suspended == 0) { XWRITE4(sc, door, XHCI_DOORBELL(index), epno | XHCI_DB_SID_SET(xfer->stream_id)); } } static void xhci_transfer_remove(struct usb_xfer *xfer, usb_error_t error) { struct xhci_endpoint_ext *pepext; if (xfer->flags_int.bandwidth_reclaimed) { xfer->flags_int.bandwidth_reclaimed = 0; pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); pepext->trb_used[xfer->stream_id]--; pepext->xfer[xfer->qh_pos] = NULL; if (error && pepext->trb_running != 0) { pepext->trb_halted = 1; pepext->trb_running = 0; } } } static usb_error_t xhci_transfer_insert(struct usb_xfer *xfer) { struct xhci_td *td_first; struct xhci_td *td_last; struct xhci_trb *trb_link; struct xhci_endpoint_ext *pepext; uint64_t addr; usb_stream_t id; uint8_t i; uint8_t inext; uint8_t trb_limit; DPRINTFN(8, "\n"); id = xfer->stream_id; /* check if already inserted */ if (xfer->flags_int.bandwidth_reclaimed) { DPRINTFN(8, "Already in schedule\n"); return (0); } pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); td_first = xfer->td_transfer_first; td_last = xfer->td_transfer_last; addr = pepext->physaddr; switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: case UE_INTERRUPT: /* single buffered */ trb_limit = 1; break; default: /* multi buffered */ trb_limit = (XHCI_MAX_TRANSFERS - 2); break; } if (pepext->trb_used[id] >= trb_limit) { DPRINTFN(8, "Too many TDs queued.\n"); return (USB_ERR_NOMEM); } /* check for stopped condition, after putting transfer on interrupt queue */ if (pepext->trb_running == 0) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); DPRINTFN(8, "Not running\n"); /* start configuration */ (void)usb_proc_msignal(USB_BUS_CONTROL_XFER_PROC(&sc->sc_bus), &sc->sc_config_msg[0], &sc->sc_config_msg[1]); return (0); } pepext->trb_used[id]++; /* get current TRB index */ i = pepext->trb_index[id]; /* get next TRB index */ inext = (i + 1); /* the last entry of the ring is a hardcoded link TRB */ if (inext >= (XHCI_MAX_TRANSFERS - 1)) inext = 0; /* store next TRB index, before stream ID offset is added */ pepext->trb_index[id] = inext; /* offset for stream */ i += id * XHCI_MAX_TRANSFERS; inext += id * XHCI_MAX_TRANSFERS; /* compute terminating return address */ addr += (inext * sizeof(struct xhci_trb)); /* compute link TRB pointer */ trb_link = td_last->td_trb + td_last->ntrb; /* update next pointer of last link TRB */ trb_link->qwTrb0 = htole64(addr); trb_link->dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); trb_link->dwTrb3 = htole32(XHCI_TRB_3_IOC_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); #ifdef USB_DEBUG xhci_dump_trb(&td_last->td_trb[td_last->ntrb]); #endif usb_pc_cpu_flush(td_last->page_cache); /* write ahead chain end marker */ pepext->trb[inext].qwTrb0 = 0; pepext->trb[inext].dwTrb2 = 0; pepext->trb[inext].dwTrb3 = 0; /* update next pointer of link TRB */ pepext->trb[i].qwTrb0 = htole64((uint64_t)td_first->td_self); pepext->trb[i].dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); #ifdef USB_DEBUG xhci_dump_trb(&pepext->trb[i]); #endif usb_pc_cpu_flush(pepext->page_cache); /* toggle cycle bit which activates the transfer chain */ pepext->trb[i].dwTrb3 = htole32(XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); usb_pc_cpu_flush(pepext->page_cache); DPRINTF("qh_pos = %u\n", i); pepext->xfer[i] = xfer; xfer->qh_pos = i; xfer->flags_int.bandwidth_reclaimed = 1; xhci_endpoint_doorbell(xfer); return (0); } static void xhci_root_intr(struct xhci_softc *sc) { uint16_t i; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* clear any old interrupt data */ memset(sc->sc_hub_idata, 0, sizeof(sc->sc_hub_idata)); for (i = 1; i <= sc->sc_noport; i++) { /* pick out CHANGE bits from the status register */ if (XREAD4(sc, oper, XHCI_PORTSC(i)) & ( XHCI_PS_CSC | XHCI_PS_PEC | XHCI_PS_OCC | XHCI_PS_WRC | XHCI_PS_PRC | XHCI_PS_PLC | XHCI_PS_CEC)) { sc->sc_hub_idata[i / 8] |= 1 << (i % 8); DPRINTF("port %d changed\n", i); } } uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); } /*------------------------------------------------------------------------* * xhci_device_done - XHCI done handler * * NOTE: This function can be called two times in a row on * the same USB transfer. From close and from interrupt. *------------------------------------------------------------------------*/ static void xhci_device_done(struct usb_xfer *xfer, usb_error_t error) { DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); /* remove transfer from HW queue */ xhci_transfer_remove(xfer, error); /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * XHCI data transfer support (generic type) *------------------------------------------------------------------------*/ static void xhci_device_generic_open(struct usb_xfer *xfer) { if (xfer->flags_int.isochronous_xfr) { switch (xfer->xroot->udev->speed) { case USB_SPEED_FULL: break; default: usb_hs_bandwidth_alloc(xfer); break; } } } static void xhci_device_generic_close(struct usb_xfer *xfer) { DPRINTF("\n"); xhci_device_done(xfer, USB_ERR_CANCELLED); if (xfer->flags_int.isochronous_xfr) { switch (xfer->xroot->udev->speed) { case USB_SPEED_FULL: break; default: usb_hs_bandwidth_free(xfer); break; } } } static void xhci_device_generic_multi_enter(struct usb_endpoint *ep, usb_stream_t stream_id, struct usb_xfer *enter_xfer) { struct usb_xfer *xfer; /* check if there is a current transfer */ xfer = ep->endpoint_q[stream_id].curr; if (xfer == NULL) return; /* * Check if the current transfer is started and then pickup * the next one, if any. Else wait for next start event due to * block on failure feature. */ if (!xfer->flags_int.bandwidth_reclaimed) return; xfer = TAILQ_FIRST(&ep->endpoint_q[stream_id].head); if (xfer == NULL) { /* * In case of enter we have to consider that the * transfer is queued by the USB core after the enter * method is called. */ xfer = enter_xfer; if (xfer == NULL) return; } /* try to multi buffer */ xhci_transfer_insert(xfer); } static void xhci_device_generic_enter(struct usb_xfer *xfer) { DPRINTF("\n"); - /* setup TD's and QH */ + /* set up TD's and QH */ xhci_setup_generic_chain(xfer); xhci_device_generic_multi_enter(xfer->endpoint, xfer->stream_id, xfer); } static void xhci_device_generic_start(struct usb_xfer *xfer) { DPRINTF("\n"); /* try to insert xfer on HW queue */ xhci_transfer_insert(xfer); /* try to multi buffer */ xhci_device_generic_multi_enter(xfer->endpoint, xfer->stream_id, NULL); /* add transfer last on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) usbd_transfer_timeout_ms(xfer, &xhci_timeout, xfer->timeout); } static const struct usb_pipe_methods xhci_device_generic_methods = { .open = xhci_device_generic_open, .close = xhci_device_generic_close, .enter = xhci_device_generic_enter, .start = xhci_device_generic_start, }; /*------------------------------------------------------------------------* * xhci root HUB support *------------------------------------------------------------------------* * Simulate a hardware HUB by handling all the necessary requests. *------------------------------------------------------------------------*/ #define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) } static const struct usb_device_descriptor xhci_devd = { .bLength = sizeof(xhci_devd), .bDescriptorType = UDESC_DEVICE, /* type */ HSETW(.bcdUSB, 0x0300), /* USB version */ .bDeviceClass = UDCLASS_HUB, /* class */ .bDeviceSubClass = UDSUBCLASS_HUB, /* subclass */ .bDeviceProtocol = UDPROTO_SSHUB, /* protocol */ .bMaxPacketSize = 9, /* max packet size */ HSETW(.idVendor, 0x0000), /* vendor */ HSETW(.idProduct, 0x0000), /* product */ HSETW(.bcdDevice, 0x0100), /* device version */ .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 0, .bNumConfigurations = 1, /* # of configurations */ }; static const struct xhci_bos_desc xhci_bosd = { .bosd = { .bLength = sizeof(xhci_bosd.bosd), .bDescriptorType = UDESC_BOS, HSETW(.wTotalLength, sizeof(xhci_bosd)), .bNumDeviceCaps = 3, }, .usb2extd = { .bLength = sizeof(xhci_bosd.usb2extd), .bDescriptorType = 1, .bDevCapabilityType = 2, .bmAttributes[0] = 2, }, .usbdcd = { .bLength = sizeof(xhci_bosd.usbdcd), .bDescriptorType = UDESC_DEVICE_CAPABILITY, .bDevCapabilityType = 3, .bmAttributes = 0, /* XXX */ HSETW(.wSpeedsSupported, 0x000C), .bFunctionalitySupport = 8, .bU1DevExitLat = 255, /* dummy - not used */ .wU2DevExitLat = { 0x00, 0x08 }, }, .cidd = { .bLength = sizeof(xhci_bosd.cidd), .bDescriptorType = 1, .bDevCapabilityType = 4, .bReserved = 0, .bContainerID = 0, /* XXX */ }, }; static const struct xhci_config_desc xhci_confd = { .confd = { .bLength = sizeof(xhci_confd.confd), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(xhci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0 /* max power */ }, .ifcd = { .bLength = sizeof(xhci_confd.ifcd), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = 0, }, .endpd = { .bLength = sizeof(xhci_confd.endpd), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | XHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 2, /* max 15 ports */ .bInterval = 255, }, .endpcd = { .bLength = sizeof(xhci_confd.endpcd), .bDescriptorType = UDESC_ENDPOINT_SS_COMP, .bMaxBurst = 0, .bmAttributes = 0, }, }; static const struct usb_hub_ss_descriptor xhci_hubd = { .bLength = sizeof(xhci_hubd), .bDescriptorType = UDESC_SS_HUB, }; static usb_error_t xhci_roothub_exec(struct usb_device *udev, struct usb_device_request *req, const void **pptr, uint16_t *plength) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); const char *str_ptr; const void *ptr; uint32_t port; uint32_t v; uint16_t len; uint16_t i; uint16_t value; uint16_t index; uint8_t j; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* buffer reset */ ptr = (const void *)&sc->sc_hub_desc; len = 0; err = 0; value = UGETW(req->wValue); index = UGETW(req->wIndex); DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", req->bmRequestType, req->bRequest, UGETW(req->wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(xhci_devd); ptr = (const void *)&xhci_devd; break; case UDESC_BOS: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(xhci_bosd); ptr = (const void *)&xhci_bosd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(xhci_confd); ptr = (const void *)&xhci_confd; break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ str_ptr = "\001"; break; case 1: /* Vendor */ str_ptr = sc->sc_vendor; break; case 2: /* Product */ str_ptr = "XHCI root HUB"; break; default: str_ptr = ""; break; } len = usb_make_str_desc( sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), str_ptr); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= XHCI_MAX_DEVICES) { err = USB_ERR_IOERROR; goto done; } break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if (value != 0 && value != 1) { err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(9, "UR_CLEAR_PORT_FEATURE\n"); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTSC(index); v = XREAD4(sc, oper, port); i = XHCI_PS_PLS_GET(v); v &= ~XHCI_PS_CLEAR; switch (value) { case UHF_C_BH_PORT_RESET: XWRITE4(sc, oper, port, v | XHCI_PS_WRC); break; case UHF_C_PORT_CONFIG_ERROR: XWRITE4(sc, oper, port, v | XHCI_PS_CEC); break; case UHF_C_PORT_SUSPEND: case UHF_C_PORT_LINK_STATE: XWRITE4(sc, oper, port, v | XHCI_PS_PLC); break; case UHF_C_PORT_CONNECTION: XWRITE4(sc, oper, port, v | XHCI_PS_CSC); break; case UHF_C_PORT_ENABLE: XWRITE4(sc, oper, port, v | XHCI_PS_PEC); break; case UHF_C_PORT_OVER_CURRENT: XWRITE4(sc, oper, port, v | XHCI_PS_OCC); break; case UHF_C_PORT_RESET: XWRITE4(sc, oper, port, v | XHCI_PS_PRC); break; case UHF_PORT_ENABLE: XWRITE4(sc, oper, port, v | XHCI_PS_PED); break; case UHF_PORT_POWER: XWRITE4(sc, oper, port, v & ~XHCI_PS_PP); break; case UHF_PORT_INDICATOR: XWRITE4(sc, oper, port, v & ~XHCI_PS_PIC_SET(3)); break; case UHF_PORT_SUSPEND: /* U3 -> U15 */ if (i == 3) { XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(0xF) | XHCI_PS_LWS); } /* wait 20ms for resume sequence to complete */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); /* U0 */ XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(0) | XHCI_PS_LWS); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } v = XREAD4(sc, capa, XHCI_HCSPARAMS0); sc->sc_hub_desc.hubd = xhci_hubd; sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; if (XHCI_HCS0_PPC(v)) i = UHD_PWR_INDIVIDUAL; else i = UHD_PWR_GANGED; if (XHCI_HCS0_PIND(v)) i |= UHD_PORT_IND; i |= UHD_OC_INDIVIDUAL; USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, i); /* see XHCI section 5.4.9: */ sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 10; for (j = 1; j <= sc->sc_noport; j++) { v = XREAD4(sc, oper, XHCI_PORTSC(j)); if (v & XHCI_PS_DR) { sc->sc_hub_desc.hubd. DeviceRemovable[j / 8] |= 1U << (j % 8); } } len = sc->sc_hub_desc.hubd.bLength; break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): len = 16; memset(sc->sc_hub_desc.temp, 0, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "UR_GET_STATUS i=%d\n", index); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } v = XREAD4(sc, oper, XHCI_PORTSC(index)); DPRINTFN(9, "port status=0x%08x\n", v); i = UPS_PORT_LINK_STATE_SET(XHCI_PS_PLS_GET(v)); switch (XHCI_PS_SPEED_GET(v)) { case 3: i |= UPS_HIGH_SPEED; break; case 2: i |= UPS_LOW_SPEED; break; case 1: /* FULL speed */ break; default: i |= UPS_OTHER_SPEED; break; } if (v & XHCI_PS_CCS) i |= UPS_CURRENT_CONNECT_STATUS; if (v & XHCI_PS_PED) i |= UPS_PORT_ENABLED; if (v & XHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; if (v & XHCI_PS_PR) i |= UPS_RESET; if (v & XHCI_PS_PP) { /* * The USB 3.0 RH is using the * USB 2.0's power bit */ i |= UPS_PORT_POWER; } USETW(sc->sc_hub_desc.ps.wPortStatus, i); i = 0; if (v & XHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; if (v & XHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; if (v & XHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if (v & XHCI_PS_WRC) i |= UPS_C_BH_PORT_RESET; if (v & XHCI_PS_PRC) i |= UPS_C_PORT_RESET; if (v & XHCI_PS_PLC) i |= UPS_C_PORT_LINK_STATE; if (v & XHCI_PS_CEC) i |= UPS_C_PORT_CONFIG_ERROR; USETW(sc->sc_hub_desc.ps.wPortChange, i); len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): i = index >> 8; index &= 0x00FF; if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTSC(index); v = XREAD4(sc, oper, port) & ~XHCI_PS_CLEAR; switch (value) { case UHF_PORT_U1_TIMEOUT: if (XHCI_PS_SPEED_GET(v) != 4) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTPMSC(index); v = XREAD4(sc, oper, port); v &= ~XHCI_PM3_U1TO_SET(0xFF); v |= XHCI_PM3_U1TO_SET(i); XWRITE4(sc, oper, port, v); break; case UHF_PORT_U2_TIMEOUT: if (XHCI_PS_SPEED_GET(v) != 4) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTPMSC(index); v = XREAD4(sc, oper, port); v &= ~XHCI_PM3_U2TO_SET(0xFF); v |= XHCI_PM3_U2TO_SET(i); XWRITE4(sc, oper, port, v); break; case UHF_BH_PORT_RESET: XWRITE4(sc, oper, port, v | XHCI_PS_WPR); break; case UHF_PORT_LINK_STATE: XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(i) | XHCI_PS_LWS); /* 4ms settle time */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); break; case UHF_PORT_ENABLE: DPRINTFN(3, "set port enable %d\n", index); break; case UHF_PORT_SUSPEND: DPRINTFN(6, "suspend port %u (LPM=%u)\n", index, i); j = XHCI_PS_SPEED_GET(v); if ((j < 1) || (j > 3)) { /* non-supported speed */ err = USB_ERR_IOERROR; goto done; } XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(i ? 2 /* LPM */ : 3) | XHCI_PS_LWS); break; case UHF_PORT_RESET: DPRINTFN(6, "reset port %d\n", index); XWRITE4(sc, oper, port, v | XHCI_PS_PR); break; case UHF_PORT_POWER: DPRINTFN(3, "set port power %d\n", index); XWRITE4(sc, oper, port, v | XHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(3, "set port test %d\n", index); break; case UHF_PORT_INDICATOR: DPRINTFN(3, "set port indicator %d\n", index); v &= ~XHCI_PS_PIC_SET(3); v |= XHCI_PS_PIC_SET(1); XWRITE4(sc, oper, port, v); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): break; default: err = USB_ERR_IOERROR; goto done; } done: *plength = len; *pptr = ptr; return (err); } static void xhci_xfer_setup(struct usb_setup_params *parm) { struct usb_page_search page_info; struct usb_page_cache *pc; struct xhci_softc *sc; struct usb_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t n; sc = XHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * The proof for the "ntd" formula is illustrated like this: * * +------------------------------------+ * | | * | |remainder -> | * | +-----+---+ | * | | xxx | x | frm 0 | * | +-----+---++ | * | | xxx | xx | frm 1 | * | +-----+----+ | * | ... | * +------------------------------------+ * * "xxx" means a completely full USB transfer descriptor * * "x" and "xx" means a short USB packet * * For the remainder of an USB transfer modulo * "max_data_length" we need two USB transfer descriptors. * One to transfer the remaining data and one to finalise with * a zero length packet in case the "force_short_xfer" flag is * set. We only need two USB transfer descriptors in the case * where the transfer length of the first one is a factor of * "max_frame_size". The rest of the needed USB transfer * descriptors is given by the buffer size divided by the * maximum data payload. */ parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 16 * 3; parm->hc_max_frame_size = XHCI_TD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); if (xfer->flags_int.isochronous_xfr) { ntd = ((1 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); } else if (xfer->flags_int.control_xfr) { ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_hc_frame_size)); } else { ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); } alloc_dma_set: if (parm->err) return; /* * Allocate queue heads and transfer descriptors */ last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(struct xhci_td), XHCI_TD_ALIGN, ntd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != ntd; n++) { struct xhci_td *td; usbd_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->td_self = page_info.physaddr; td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static usb_error_t xhci_configure_reset_endpoint(struct usb_xfer *xfer) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); struct usb_page_search buf_inp; struct usb_device *udev; struct xhci_endpoint_ext *pepext; struct usb_endpoint_descriptor *edesc; struct usb_page_cache *pcinp; usb_error_t err; usb_stream_t stream_id; uint8_t index; uint8_t epno; pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); udev = xfer->xroot->udev; index = udev->controller_slot_id; pcinp = &sc->sc_hw.devs[index].input_pc; usbd_get_page(pcinp, 0, &buf_inp); edesc = xfer->endpoint->edesc; epno = edesc->bEndpointAddress; stream_id = xfer->stream_id; if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); if (epno == 0) return (USB_ERR_NO_PIPE); /* invalid */ XHCI_CMD_LOCK(sc); /* configure endpoint */ err = xhci_configure_endpoint_by_xfer(xfer); if (err != 0) { XHCI_CMD_UNLOCK(sc); return (err); } /* * Get the endpoint into the stopped state according to the * endpoint context state diagram in the XHCI specification: */ err = xhci_cmd_stop_ep(sc, 0, epno, index); if (err != 0) DPRINTF("Could not stop endpoint %u\n", epno); err = xhci_cmd_reset_ep(sc, 0, epno, index); if (err != 0) DPRINTF("Could not reset endpoint %u\n", epno); err = xhci_cmd_set_tr_dequeue_ptr(sc, (pepext->physaddr + (stream_id * sizeof(struct xhci_trb) * XHCI_MAX_TRANSFERS)) | XHCI_EPCTX_2_DCS_SET(1), stream_id, epno, index); if (err != 0) DPRINTF("Could not set dequeue ptr for endpoint %u\n", epno); /* * Get the endpoint into the running state according to the * endpoint context state diagram in the XHCI specification: */ xhci_configure_mask(udev, (1U << epno) | 1U, 0); err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index); if (err != 0) DPRINTF("Could not configure endpoint %u\n", epno); err = xhci_cmd_configure_ep(sc, buf_inp.physaddr, 0, index); if (err != 0) DPRINTF("Could not configure endpoint %u\n", epno); XHCI_CMD_UNLOCK(sc); return (0); } static void xhci_xfer_unsetup(struct usb_xfer *xfer) { return; } static void xhci_start_dma_delay(struct usb_xfer *xfer) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); /* put transfer on interrupt queue (again) */ usbd_transfer_enqueue(&sc->sc_bus.intr_q, xfer); (void)usb_proc_msignal(USB_BUS_CONTROL_XFER_PROC(&sc->sc_bus), &sc->sc_config_msg[0], &sc->sc_config_msg[1]); } static void xhci_configure_msg(struct usb_proc_msg *pm) { struct xhci_softc *sc; struct xhci_endpoint_ext *pepext; struct usb_xfer *xfer; sc = XHCI_BUS2SC(((struct usb_bus_msg *)pm)->bus); restart: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); if ((pepext->trb_halted != 0) || (pepext->trb_running == 0)) { uint16_t i; /* clear halted and running */ pepext->trb_halted = 0; pepext->trb_running = 0; /* nuke remaining buffered transfers */ for (i = 0; i != (XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS); i++) { /* * NOTE: We need to use the timeout * error code here else existing * isochronous clients can get * confused: */ if (pepext->xfer[i] != NULL) { xhci_device_done(pepext->xfer[i], USB_ERR_TIMEOUT); } } /* * NOTE: The USB transfer cannot vanish in * this state! */ USB_BUS_UNLOCK(&sc->sc_bus); xhci_configure_reset_endpoint(xfer); USB_BUS_LOCK(&sc->sc_bus); /* check if halted is still cleared */ if (pepext->trb_halted == 0) { pepext->trb_running = 1; memset(pepext->trb_index, 0, sizeof(pepext->trb_index)); } goto restart; } if (xfer->flags_int.did_dma_delay) { /* remove transfer from interrupt queue (again) */ usbd_transfer_dequeue(xfer); /* we are finally done */ usb_dma_delay_done_cb(xfer); /* queue changed - restart */ goto restart; } } TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* try to insert xfer on HW queue */ xhci_transfer_insert(xfer); /* try to multi buffer */ xhci_device_generic_multi_enter(xfer->endpoint, xfer->stream_id, NULL); } } static void xhci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { struct xhci_endpoint_ext *pepext; DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d\n", ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode); if (udev->parent_hub == NULL) { /* root HUB has special endpoint handling */ return; } ep->methods = &xhci_device_generic_methods; pepext = xhci_get_endpoint_ext(udev, edesc); USB_BUS_LOCK(udev->bus); pepext->trb_halted = 1; pepext->trb_running = 0; USB_BUS_UNLOCK(udev->bus); } static void xhci_ep_uninit(struct usb_device *udev, struct usb_endpoint *ep) { } static void xhci_ep_clear_stall(struct usb_device *udev, struct usb_endpoint *ep) { struct xhci_endpoint_ext *pepext; DPRINTF("\n"); if (udev->flags.usb_mode != USB_MODE_HOST) { /* not supported */ return; } if (udev->parent_hub == NULL) { /* root HUB has special endpoint handling */ return; } pepext = xhci_get_endpoint_ext(udev, ep->edesc); USB_BUS_LOCK(udev->bus); pepext->trb_halted = 1; pepext->trb_running = 0; USB_BUS_UNLOCK(udev->bus); } static usb_error_t xhci_device_init(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); usb_error_t err; uint8_t temp; /* no init for root HUB */ if (udev->parent_hub == NULL) return (0); XHCI_CMD_LOCK(sc); /* set invalid default */ udev->controller_slot_id = sc->sc_noslot + 1; /* try to get a new slot ID from the XHCI */ err = xhci_cmd_enable_slot(sc, &temp); if (err) { XHCI_CMD_UNLOCK(sc); return (err); } if (temp > sc->sc_noslot) { XHCI_CMD_UNLOCK(sc); return (USB_ERR_BAD_ADDRESS); } if (sc->sc_hw.devs[temp].state != XHCI_ST_DISABLED) { DPRINTF("slot %u already allocated.\n", temp); XHCI_CMD_UNLOCK(sc); return (USB_ERR_BAD_ADDRESS); } /* store slot ID for later reference */ udev->controller_slot_id = temp; /* reset data structure */ memset(&sc->sc_hw.devs[temp], 0, sizeof(sc->sc_hw.devs[0])); /* set mark slot allocated */ sc->sc_hw.devs[temp].state = XHCI_ST_ENABLED; err = xhci_alloc_device_ext(udev); XHCI_CMD_UNLOCK(sc); /* get device into default state */ if (err == 0) err = xhci_set_address(udev, NULL, 0); return (err); } static void xhci_device_uninit(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; /* no init for root HUB */ if (udev->parent_hub == NULL) return; XHCI_CMD_LOCK(sc); index = udev->controller_slot_id; if (index <= sc->sc_noslot) { xhci_cmd_disable_slot(sc, index); sc->sc_hw.devs[index].state = XHCI_ST_DISABLED; /* free device extension */ xhci_free_device_ext(udev); } XHCI_CMD_UNLOCK(sc); } static void xhci_get_dma_delay(struct usb_device *udev, uint32_t *pus) { /* * Wait until the hardware has finished any possible use of * the transfer descriptor(s) */ *pus = 2048; /* microseconds */ } static void xhci_device_resume(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; uint8_t n; uint8_t p; DPRINTF("\n"); /* check for root HUB */ if (udev->parent_hub == NULL) return; index = udev->controller_slot_id; XHCI_CMD_LOCK(sc); /* blindly resume all endpoints */ USB_BUS_LOCK(udev->bus); for (n = 1; n != XHCI_MAX_ENDPOINTS; n++) { for (p = 0; p != XHCI_MAX_STREAMS; p++) { XWRITE4(sc, door, XHCI_DOORBELL(index), n | XHCI_DB_SID_SET(p)); } } USB_BUS_UNLOCK(udev->bus); XHCI_CMD_UNLOCK(sc); } static void xhci_device_suspend(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; uint8_t n; usb_error_t err; DPRINTF("\n"); /* check for root HUB */ if (udev->parent_hub == NULL) return; index = udev->controller_slot_id; XHCI_CMD_LOCK(sc); /* blindly suspend all endpoints */ for (n = 1; n != XHCI_MAX_ENDPOINTS; n++) { err = xhci_cmd_stop_ep(sc, 1, n, index); if (err != 0) { DPRINTF("Failed to suspend endpoint " "%u on slot %u (ignored).\n", n, index); } } XHCI_CMD_UNLOCK(sc); } static void xhci_set_hw_power(struct usb_bus *bus) { DPRINTF("\n"); } static void xhci_device_state_change(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_inp; usb_error_t err; uint8_t index; /* check for root HUB */ if (udev->parent_hub == NULL) return; index = udev->controller_slot_id; DPRINTF("\n"); if (usb_get_device_state(udev) == USB_STATE_CONFIGURED) { err = uhub_query_info(udev, &sc->sc_hw.devs[index].nports, &sc->sc_hw.devs[index].tt); if (err != 0) sc->sc_hw.devs[index].nports = 0; } XHCI_CMD_LOCK(sc); switch (usb_get_device_state(udev)) { case USB_STATE_POWERED: if (sc->sc_hw.devs[index].state == XHCI_ST_DEFAULT) break; /* set default state */ sc->sc_hw.devs[index].state = XHCI_ST_DEFAULT; /* reset number of contexts */ sc->sc_hw.devs[index].context_num = 0; err = xhci_cmd_reset_dev(sc, index); if (err != 0) { DPRINTF("Device reset failed " "for slot %u.\n", index); } break; case USB_STATE_ADDRESSED: if (sc->sc_hw.devs[index].state == XHCI_ST_ADDRESSED) break; sc->sc_hw.devs[index].state = XHCI_ST_ADDRESSED; err = xhci_cmd_configure_ep(sc, 0, 1, index); if (err) { DPRINTF("Failed to deconfigure " "slot %u.\n", index); } break; case USB_STATE_CONFIGURED: if (sc->sc_hw.devs[index].state == XHCI_ST_CONFIGURED) break; /* set configured state */ sc->sc_hw.devs[index].state = XHCI_ST_CONFIGURED; /* reset number of contexts */ sc->sc_hw.devs[index].context_num = 0; usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp); xhci_configure_mask(udev, 3, 0); err = xhci_configure_device(udev); if (err != 0) { DPRINTF("Could not configure device " "at slot %u.\n", index); } err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index); if (err != 0) { DPRINTF("Could not evaluate device " "context at slot %u.\n", index); } break; default: break; } XHCI_CMD_UNLOCK(sc); } static usb_error_t xhci_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode) { switch (ep_mode) { case USB_EP_MODE_DEFAULT: return (0); case USB_EP_MODE_STREAMS: if (xhcistreams == 0 || (ep->edesc->bmAttributes & UE_XFERTYPE) != UE_BULK || udev->speed != USB_SPEED_SUPER) return (USB_ERR_INVAL); return (0); default: return (USB_ERR_INVAL); } } static const struct usb_bus_methods xhci_bus_methods = { .endpoint_init = xhci_ep_init, .endpoint_uninit = xhci_ep_uninit, .xfer_setup = xhci_xfer_setup, .xfer_unsetup = xhci_xfer_unsetup, .get_dma_delay = xhci_get_dma_delay, .device_init = xhci_device_init, .device_uninit = xhci_device_uninit, .device_resume = xhci_device_resume, .device_suspend = xhci_device_suspend, .set_hw_power = xhci_set_hw_power, .roothub_exec = xhci_roothub_exec, .xfer_poll = xhci_do_poll, .start_dma_delay = xhci_start_dma_delay, .set_address = xhci_set_address, .clear_stall = xhci_ep_clear_stall, .device_state_change = xhci_device_state_change, .set_hw_power_sleep = xhci_set_hw_power_sleep, .set_endpoint_mode = xhci_set_endpoint_mode, }; Index: head/sys/dev/usb/controller/xhci_pci.c =================================================================== --- head/sys/dev/usb/controller/xhci_pci.c (revision 276716) +++ head/sys/dev/usb/controller/xhci_pci.c (revision 276717) @@ -1,375 +1,373 @@ /*- * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usb_if.h" static device_probe_t xhci_pci_probe; static device_attach_t xhci_pci_attach; static device_detach_t xhci_pci_detach; static usb_take_controller_t xhci_pci_take_controller; static device_method_t xhci_device_methods[] = { /* device interface */ DEVMETHOD(device_probe, xhci_pci_probe), DEVMETHOD(device_attach, xhci_pci_attach), DEVMETHOD(device_detach, xhci_pci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(usb_take_controller, xhci_pci_take_controller), DEVMETHOD_END }; static driver_t xhci_driver = { .name = "xhci", .methods = xhci_device_methods, .size = sizeof(struct xhci_softc), }; static devclass_t xhci_devclass; DRIVER_MODULE(xhci, pci, xhci_driver, xhci_devclass, 0, 0); MODULE_DEPEND(xhci, usb, 1, 1, 1); static const char * xhci_pci_match(device_t self) { uint32_t device_id = pci_get_devid(self); switch (device_id) { case 0x01941033: return ("NEC uPD720200 USB 3.0 controller"); case 0x10421b21: return ("ASMedia ASM1042 USB 3.0 controller"); case 0x0f358086: return ("Intel Intel BayTrail USB 3.0 controller"); case 0x9c318086: case 0x1e318086: return ("Intel Panther Point USB 3.0 controller"); case 0x8c318086: return ("Intel Lynx Point USB 3.0 controller"); case 0x8cb18086: return ("Intel Wildcat Point USB 3.0 controller"); default: break; } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCIP_SERIALBUS_USB_XHCI)) { return ("XHCI (generic) USB 3.0 controller"); } return (NULL); /* dunno */ } static int xhci_pci_probe(device_t self) { const char *desc = xhci_pci_match(self); if (desc) { device_set_desc(self, desc); return (0); } else { return (ENXIO); } } static int xhci_use_msi = 1; TUNABLE_INT("hw.usb.xhci.msi", &xhci_use_msi); 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_port_route(device_t self, uint32_t set, uint32_t clear) { uint32_t temp; uint32_t usb3_mask; uint32_t usb2_mask; temp = pci_read_config(self, PCI_XHCI_INTEL_USB3_PSSEN, 4) | pci_read_config(self, PCI_XHCI_INTEL_XUSB2PR, 4); temp |= set; temp &= ~clear; /* Don't set bits which the hardware doesn't support */ usb3_mask = pci_read_config(self, PCI_XHCI_INTEL_USB3PRM, 4); usb2_mask = pci_read_config(self, PCI_XHCI_INTEL_USB2PRM, 4); pci_write_config(self, PCI_XHCI_INTEL_USB3_PSSEN, temp & usb3_mask, 4); pci_write_config(self, PCI_XHCI_INTEL_XUSB2PR, temp & usb2_mask, 4); device_printf(self, "Port routing mask set to 0x%08x\n", temp); return (0); } static int xhci_pci_attach(device_t self) { struct xhci_softc *sc = device_get_softc(self); int count, err, rid; - /* XXX check for 64-bit capability */ - - if (xhci_init(sc, self)) { - device_printf(self, "Could not initialize softc\n"); - goto error; - } - - pci_enable_busmaster(self); - rid = PCI_XHCI_CBMEM; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); - goto error; + return (ENOMEM); } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); + if (xhci_init(sc, self)) { + device_printf(self, "Could not initialize softc\n"); + bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM, + sc->sc_io_res); + return (ENXIO); + } + + pci_enable_busmaster(self); + usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0); rid = 0; if (xhci_use_msi) { count = 1; if (pci_alloc_msi(self, &count) == 0) { if (bootverbose) device_printf(self, "MSI enabled\n"); rid = 1; } } sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); if (sc->sc_irq_res == NULL) { pci_release_msi(self); device_printf(self, "Could not allocate IRQ\n"); /* goto error; FALLTHROUGH - use polling */ } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (sc->sc_bus.bdev == NULL) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); sprintf(sc->sc_vendor, "0x%04x", pci_get_vendor(self)); 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) { bus_release_resource(self, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); sc->sc_irq_res = NULL; pci_release_msi(self); 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) { if (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); } else goto error; } /* On Intel chipsets reroute ports from EHCI to XHCI controller. */ switch (pci_get_devid(self)) { case 0x0f358086: /* BayTrail */ case 0x9c318086: /* Panther Point */ case 0x1e318086: /* Panther Point */ case 0x8c318086: /* Lynx Point */ case 0x8cb18086: /* Wildcat Point */ sc->sc_port_route = &xhci_pci_port_route; sc->sc_imod_default = XHCI_IMOD_DEFAULT_LP; break; default: break; } xhci_pci_take_controller(self); err = xhci_halt_controller(sc); if (err == 0) err = xhci_start_controller(sc); if (err == 0) err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(self, "XHCI halt/start/probe failed err=%d\n", err); goto error; } return (0); error: xhci_pci_detach(self); return (ENXIO); } static int xhci_pci_detach(device_t self) { struct xhci_softc *sc = device_get_softc(self); device_t bdev; if (sc->sc_bus.bdev != NULL) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* 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); - } + usb_callout_drain(&sc->sc_callout); + xhci_halt_controller(sc); pci_disable_busmaster(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); sc->sc_irq_res = NULL; pci_release_msi(self); } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM, sc->sc_io_res); sc->sc_io_res = NULL; } xhci_uninit(sc); return (0); } static int xhci_pci_take_controller(device_t self) { struct xhci_softc *sc = device_get_softc(self); uint32_t cparams; uint32_t eecp; uint32_t eec; uint16_t to; uint8_t bios_sem; cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0); eec = -1; /* Synchronise with the BIOS if it owns the controller. */ for (eecp = XHCI_HCS0_XECP(cparams) << 2; eecp != 0 && XHCI_XECP_NEXT(eec); eecp += XHCI_XECP_NEXT(eec) << 2) { eec = XREAD4(sc, capa, eecp); if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY) continue; bios_sem = XREAD1(sc, capa, eecp + XHCI_XECP_BIOS_SEM); if (bios_sem == 0) continue; device_printf(sc->sc_bus.bdev, "waiting for BIOS " "to give up control\n"); XWRITE1(sc, capa, eecp + XHCI_XECP_OS_SEM, 1); to = 500; while (1) { bios_sem = XREAD1(sc, capa, eecp + XHCI_XECP_BIOS_SEM); if (bios_sem == 0) break; if (--to == 0) { device_printf(sc->sc_bus.bdev, "timed out waiting for BIOS\n"); break; } usb_pause_mtx(NULL, hz / 100); /* wait 10ms */ } } return (0); } Index: head/sys/dev/usb/usb_bus.h =================================================================== --- head/sys/dev/usb/usb_bus.h (revision 276716) +++ head/sys/dev/usb/usb_bus.h (revision 276717) @@ -1,120 +1,121 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _USB_BUS_H_ #define _USB_BUS_H_ /* * The following structure defines the USB explore message sent to the USB * explore process. */ struct usb_bus_msg { struct usb_proc_msg hdr; struct usb_bus *bus; }; /* * The following structure defines the USB statistics structure. */ struct usb_bus_stat { uint32_t uds_requests[4]; }; /* * The following structure defines an USB BUS. There is one USB BUS * for every Host or Device controller. */ struct usb_bus { struct usb_bus_stat stats_err; struct usb_bus_stat stats_ok; #if USB_HAVE_ROOT_MOUNT_HOLD struct root_hold_token *bus_roothold; #endif #if USB_HAVE_PER_BUS_PROCESS #define USB_BUS_GIANT_PROC(bus) (&(bus)->giant_callback_proc) #define USB_BUS_NON_GIANT_PROC(bus) (&(bus)->non_giant_callback_proc) #define USB_BUS_EXPLORE_PROC(bus) (&(bus)->explore_proc) #define USB_BUS_CONTROL_XFER_PROC(bus) (&(bus)->control_xfer_proc) /* * There are two callback processes. One for Giant locked * callbacks. One for non-Giant locked callbacks. This should * avoid congestion and reduce response time in most cases. */ struct usb_process giant_callback_proc; struct usb_process non_giant_callback_proc; /* Explore process */ struct usb_process explore_proc; /* Control request process */ struct usb_process control_xfer_proc; #endif struct usb_bus_msg explore_msg[2]; struct usb_bus_msg detach_msg[2]; struct usb_bus_msg attach_msg[2]; struct usb_bus_msg suspend_msg[2]; struct usb_bus_msg resume_msg[2]; struct usb_bus_msg reset_msg[2]; struct usb_bus_msg shutdown_msg[2]; /* * This mutex protects the USB hardware: */ struct mtx bus_mtx; struct mtx bus_spin_lock; struct usb_xfer_queue intr_q; struct usb_callout power_wdog; /* power management */ device_t parent; device_t bdev; /* filled by HC driver */ #if USB_HAVE_BUSDMA struct usb_dma_parent_tag dma_parent_tag[1]; struct usb_dma_tag dma_tags[USB_BUS_DMA_TAG_MAX]; #endif const struct usb_bus_methods *methods; /* filled by HC driver */ struct usb_device **devices; struct ifnet *ifp; /* only for USB Packet Filter */ usb_power_mask_t hw_power_state; /* see USB_HW_POWER_XXX */ usb_size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; uint16_t isoc_time_last; /* in milliseconds */ uint8_t alloc_failed; /* Set if memory allocation failed. */ uint8_t driver_added_refcount; /* Current driver generation count */ enum usb_revision usbrev; /* USB revision. See "USB_REV_XXX". */ uint8_t devices_max; /* maximum number of USB devices */ uint8_t do_probe; /* set if USB should be re-probed */ uint8_t no_explore; /* don't explore USB ports */ + uint8_t dma_bits; /* number of DMA address lines */ }; #endif /* _USB_BUS_H_ */ Index: head/sys/dev/usb/usb_transfer.c =================================================================== --- head/sys/dev/usb/usb_transfer.c (revision 276716) +++ head/sys/dev/usb/usb_transfer.c (revision 276717) @@ -1,3457 +1,3458 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ struct usb_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb_request_callback; static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usbd_update_max_frame_size(struct usb_xfer *); static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usbd_control_transfer_init(struct usb_xfer *); static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb_request_callback *------------------------------------------------------------------------*/ static void usb_request_callback(struct usb_xfer *xfer, usb_error_t error) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb_handle_request_callback(xfer, error); else usbd_do_request_callback(xfer, error); } /*------------------------------------------------------------------------* * usbd_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ /* this computation should not overflow 16-bit */ /* max = 15 * 1024 */ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* * usbd_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usbd_get_dma_delay(struct usb_device *udev) { const struct usb_bus_methods *mtod; uint32_t temp; mtod = udev->bus->methods; temp = 0; if (mtod->get_dma_delay) { (mtod->get_dma_delay) (udev, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * If the "align" argument is equal to "1" a non-contiguous allocation * can happen. Else if the "align" argument is greater than "1", the * allocation will always be contiguous in memory. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; usb_size_t n_dma_pc; usb_size_t n_dma_pg; usb_size_t n_obj; usb_size_t x; usb_size_t y; usb_size_t r; usb_size_t z; USB_ASSERT(align > 0, ("Invalid alignment, 0x%08x\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (align == 1) { /* special case - non-cached multi page DMA memory */ n_dma_pc = count; n_dma_pg = (2 + (size / USB_PAGE_SIZE)); n_obj = 1; } else if (size >= USB_PAGE_SIZE) { n_dma_pc = count; n_dma_pg = 1; n_obj = 1; } else { /* compute number of objects per page */ n_obj = (USB_PAGE_SIZE / size); /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = ((count + n_obj - 1) / n_obj); n_dma_pg = 1; } /* * DMA memory is allocated once, but mapped twice. That's why * there is one list for auto-free and another list for * non-auto-free which only holds the mapping and not the * allocation. */ if (parm->buf == NULL) { /* reserve memory (auto-free) */ parm->dma_page_ptr += n_dma_pc * n_dma_pg; parm->dma_page_cache_ptr += n_dma_pc; /* reserve memory (no-auto-free) */ parm->dma_page_ptr += count * n_dma_pg; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } if (ppc) { *ppc = parm->xfer_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and one page */ parm->dma_page_cache_ptr++; pg += n_dma_pg; for (y = 0; (y != n_obj); y++, r--, pc++, pg += n_dma_pg) { /* Load sub-chunk into DMA */ if (usb_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usbd_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usbd_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint16_t maxp_old; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->endpoint->edesc; ecomp = xfer->endpoint->ecomp; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpointno = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; switch (parm->speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (xfer->max_packet_count > 3) xfer->max_packet_count = 3; break; default: break; } xfer->max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; if (ecomp != NULL) xfer->max_packet_count += ecomp->bMaxBurst; if ((xfer->max_packet_count == 0) || (xfer->max_packet_count > 16)) xfer->max_packet_count = 16; switch (type) { case UE_CONTROL: xfer->max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = UE_GET_SS_ISO_MULT( ecomp->bmAttributes) + 1; if (mult > 3) mult = 3; xfer->max_packet_count *= mult; } break; default: break; } xfer->max_packet_size &= 0x7FF; break; default: break; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* store max packet size value before filtering */ maxp_old = xfer->max_packet_size; /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usbd_get_std_packet_size(&std_size, type, parm->speed); if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* * Check if the max packet size was outside its allowed range * and clamped to a valid value: */ if (maxp_old != xfer->max_packet_size) xfer->flags_int.maxp_was_clamped = 1; /* compute "max_frame_size" */ usbd_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = 0; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = edesc->bInterval; if (xfer->fps_shift > 0) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; if (xfer->flags.pre_scale_frames != 0) xfer->nframes <<= (3 - xfer->fps_shift); break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * If a value is specified use that else check the * endpoint descriptor! */ if (type == UE_INTERRUPT) { uint32_t temp; if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1 << (16 - 4)); else xfer->interval = (1 << (xfer->interval - 4)); break; } } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } xfer->fps_shift = 0; temp = 1; while ((temp != 0) && (temp < xfer->interval)) { xfer->fps_shift++; temp *= 2; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: xfer->fps_shift += 3; break; } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence * this leads to alot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usbd_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* * Setup "frlengths" and shadow "frlengths" for keeping the * initial frame lengths when a USB transfer is complete. This * information is useful when computing isochronous offsets. */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { #if USB_HAVE_BUSDMA struct usb_page_search page_info; struct usb_page_cache *pc; if (usbd_transfer_setup_sub_malloc(parm, &pc, parm->bufsize, 1, 1)) { parm->err = USB_ERR_NOMEM; } else if (parm->buf != NULL) { usbd_get_page(pc, 0, &page_info); xfer->local_buffer = page_info.buffer; usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } #else /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf != NULL) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); #endif } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } /*------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usbd_transfer_unsetup" after calling * "usbd_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; struct usb_setup_params *parm; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; usb_error_t error = 0; uint16_t n; uint16_t refcount; uint8_t do_unlock; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == 0) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* more sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { error = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { error = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (error) return (error); /* Protect scratch area */ do_unlock = usbd_enum_lock(udev); refcount = 0; info = NULL; parm = &udev->scratch.xfer_setup[0].parm; memset(parm, 0, sizeof(*parm)); parm->udev = udev; parm->speed = usbd_get_speed(udev); parm->hc_max_packet_count = 1; if (parm->speed >= USB_SPEED_MAX) { parm->err = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm->size[0]; #if USB_HAVE_BUSDMA info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]); #endif info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag, - xfer_mtx, &usb_bdma_done_event, 32, parm->dma_tag_max); + xfer_mtx, &usb_bdma_done_event, udev->bus->dma_bits, + parm->dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usbd_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb_control_ep_cfg) info->done_p = USB_BUS_CONTROL_XFER_PROC(udev->bus); else if (xfer_mtx == &Giant) info->done_p = USB_BUS_GIANT_PROC(udev->bus); else info->done_p = USB_BUS_NON_GIANT_PROC(udev->bus); } /* reset sizes */ parm->size[0] = 0; parm->buf = buf; parm->size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ ep = usbd_get_endpoint(udev, ifaces[setup->if_index], setup); /* * Check that the USB PIPE is valid and that * the endpoint mode is proper. * * Make sure we don't allocate a streams * transfer when such a combination is not * valid. */ if ((ep == NULL) || (ep->methods == NULL) || ((ep->ep_mode != USB_EP_MODE_STREAMS) && (ep->ep_mode != USB_EP_MODE_DEFAULT)) || (setup->stream_id != 0 && (setup->stream_id >= USB_MAX_EP_STREAMS || (ep->ep_mode != USB_EP_MODE_STREAMS)))) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm->err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm->curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm->size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &udev->scratch.xfer_setup[0].dummy; memset(xfer, 0, sizeof(*xfer)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; /* set transfer stream ID */ xfer->stream_id = setup->stream_id; parm->size[0] += sizeof(xfer[0]); parm->methods = xfer->endpoint->methods; parm->curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (parm); /* check for error */ if (parm->err) goto done; if (buf) { /* * Increment the endpoint refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "endpoint->refcount_alloc" if you * want more information. */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) parm->err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; if (xfer->endpoint->refcount_alloc == 0) panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); USB_BUS_UNLOCK(info->bus); /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } /* check for error */ if (parm->err) goto done; } if (buf != NULL || parm->err != 0) goto done; /* if no transfers, nothing to do */ if (refcount == 0) goto done; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[1] = parm->size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is three: * 1) for loading memory * 2) for allocating memory * 3) for fixing memory [UHCI] */ parm->dma_tag_max += 3 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm->dma_tag_max += 8; parm->dma_tag_p += parm->dma_tag_max; parm->size[0] += ((uint8_t *)parm->dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[3] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[4] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[5] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[2] = parm->size[0]; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); parm->size[6] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm->err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm->size[0]); goto done; } parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]); parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]); parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]); parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]); parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usbd_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usbd_transfer_unsetup_sub(info, 0); } } /* check if any errors happened */ if (parm->err) usbd_transfer_unsetup(ppxfer, n_setup); error = parm->err; if (do_unlock) usbd_enum_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { #if USB_HAVE_BUSDMA struct usb_page_cache *pc; #endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usbd_get_dma_delay(info->udev); if (temp != 0) { usb_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } } /* make sure that our done messages are not queued anywhere */ usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb_dma_tag_unsetup(&info->dma_parent_tag); #endif cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usbd_transfer_start(sc->pxfer[0]); * usbd_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usbd_transfer_start and * usbd_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usbd_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default endpoint does not have an * interface, even if endpoint->iface_index == 0 */ USB_BUS_LOCK(info->bus); xfer->endpoint->refcount_alloc--; USB_BUS_UNLOCK(info->bus); usb_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usbd_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usbd_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usbd_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpointno |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usbd_setup_ctrl_transfer(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usbd_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usbd_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usbd_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length (%d) greater than " "remaining length (%d)\n", len, xfer->flags_int.control_rem); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_transfer_submit - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usbd_transfer_submit(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n", xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #ifdef USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb_dump_endpoint(xfer->endpoint); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usbd_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usbd_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usbd_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb_command_wrapper(&xfer->endpoint-> endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { /* make a copy of the frlenghts[] */ xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usbd_setup_ctrl_transfer(xfer)) { USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usbd_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usbd_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usbd_pipe_enter(struct usb_xfer *xfer) { struct usb_endpoint *ep; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); ep = xfer->endpoint; DPRINTF("enter\n"); /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* enter the transfer */ (ep->methods->enter) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usbd_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usbd_transfer_drain()". *------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usb_xfer *xfer) { struct usb_endpoint *ep; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { if (xfer->flags_int.started) { /* nothing to do except clearing the "started" flag */ /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); /* override any previous error */ xfer->error = USB_ERR_CANCELLED; /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->endpoint->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->endpoint->methods->close) (xfer); /* * Any additional DMA delay is done by * "usbd_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * endpoint. */ ep = xfer->endpoint; /* * If the current USB transfer is completing we need * to start the next one: */ if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper( &ep->endpoint_q[xfer->stream_id], NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usbd_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usbd_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usbd_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usbd_transfer_stop(xfer); while (usbd_transfer_pending(xfer) || xfer->flags_int.doing_callback) { /* * It is allowed that the callback can drop its * transfer mutex. In that case checking only * "usbd_transfer_pending()" is not enough to tell if * the USB transfer is fully drained. We also need to * check the internal "doing_callback" flag. */ xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (&xfer->frbuffers[frindex]); } void * usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex) { struct usb_page_search page_info; KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info); return (page_info.buffer); } /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * * The following function is only useful for isochronous transfers. It * returns how many times the frame execution rate has been shifted * down. * * Return value: * Success: 0..3 * Failure: 0 *------------------------------------------------------------------------*/ uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer) { return (xfer->fps_shift); } usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; usbd_xfer_set_frame_len(xfer, frindex, len); } void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); if (ptr != NULL) *ptr = xfer->frbuffers[frindex].buffer; if (len != NULL) *len = xfer->frlengths[frindex]; } /*------------------------------------------------------------------------* * usbd_xfer_old_frame_length * * This function returns the framelength of the given frame at the * time the transfer was submitted. This function can be used to * compute the starting data pointer of the next isochronous frame * when an isochronous transfer has completed. *------------------------------------------------------------------------*/ usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex + xfer->max_frame_count]); } void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) { if (actlen != NULL) *actlen = xfer->actlen; if (sumlen != NULL) *sumlen = xfer->sumlen; if (aframes != NULL) *aframes = xfer->aframes; if (nframes != NULL) *nframes = xfer->nframes; } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external\n")); KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } void usbd_xfer_set_interval(struct usb_xfer *xfer, int i) { xfer->interval = i; } void usbd_xfer_set_timeout(struct usb_xfer *xfer, int t) { xfer->timeout = t; } void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n) { xfer->nframes = n; } usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer) { return (xfer->max_frame_count); } usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer) { return (xfer->max_data_length); } usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer) { return (xfer->max_frame_size); } void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); xfer->frlengths[frindex] = len; } /*------------------------------------------------------------------------* * usb_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->xfer_mtx); } /*------------------------------------------------------------------------* * usbd_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usbd_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usbd_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usbd_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if (!mtx_owned(info->xfer_mtx) && !SCHEDULER_STOPPED()) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. */ DPRINTFN(3, "case 5\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; /* set flag in case of drain */ xfer->flags_int.doing_callback = 1; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usbd_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usbd_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb_bdma_post_sync(xfer); } #endif } } #if USB_HAVE_PF if (xfer->usb_state != USB_ST_SETUP) usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); #endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb_state == USB_ST_ERROR)) { /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* try to loop, but not recursivly */ usb_command_wrapper(&info->done_q, xfer); return; } done: /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usbd_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is ususally only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ void usb_dma_delay_done_cb(struct usb_xfer *xfer) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usbd_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usbd_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usbd_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usbd_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error) { struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); DPRINTF("err=%s\n", usbd_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); /* end of control transfer, if any */ xfer->flags_int.control_act = 0; return; } /* only set transfer error, if not already set */ if (xfer->error == USB_ERR_NORMAL_COMPLETION) xfer->error = error; /* stop any callouts */ usb_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usbd_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(info->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &info->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error) { info->bus->stats_err.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else { info->bus->stats_ok.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usbd_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usbd_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_endpoint *ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_xfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_xfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } int usbd_xfer_is_stalled(struct usb_xfer *xfer) { return (xfer->endpoint->is_stalled); } /*------------------------------------------------------------------------* * usbd_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usbd_pipe_start(struct usb_xfer_queue *pq) { struct usb_endpoint *ep; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the endpoint is already stalled we do nothing ! */ if (ep->is_stalled) { return; } /* * Check if we are supposed to stall the endpoint: */ if (xfer->flags.stall_pipe) { struct usb_device *udev; struct usb_xfer_root *info; /* clear stall command */ xfer->flags.stall_pipe = 0; /* get pointer to USB device */ info = xfer->xroot; udev = info->udev; /* * Only stall BULK and INTERRUPT endpoints. */ type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { uint8_t did_stall; did_stall = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, ep, &did_stall); } else if (udev->ctrl_xfer[1]) { info = udev->ctrl_xfer[1]->xroot; usb_proc_msignal( USB_BUS_NON_GIANT_PROC(info->bus), &udev->cs_msg[0], &udev->cs_msg[1]); } else { /* should not happen */ DPRINTFN(0, "No stall handler\n"); } /* * Check if we should stall. Some USB hardware * handles set- and clear-stall in hardware. */ if (did_stall) { /* * The transfer will be continued when * the clear-stall control endpoint * message is received. */ ep->is_stalled = 1; return; } } else if (type == UE_ISOCHRONOUS) { /* * Make sure any FIFO overflow or other FIFO * error conditions go away by resetting the * endpoint FIFO through the clear stall * method. */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->clear_stall) (udev, ep); } } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usbd_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usbd_transfer_timeout_ms(xfer, &usbd_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usbd_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer); } /*------------------------------------------------------------------------* * usbd_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * ep transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; struct usb_bus *bus; usb_frcount_t x; bus = xfer->xroot->bus; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && (xfer->error == USB_ERR_CANCELLED || xfer->error == USB_ERR_TIMEOUT || bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usbd_get_dma_delay(xfer->xroot->udev); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(bus); /* * Some hardware solutions have dedicated * events when it is safe to free DMA'ed * memory. For the other hardware platforms we * use a static delay. */ if (bus->methods->start_dma_delay != NULL) { (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, (void (*)(void *))&usb_dma_delay_done_cb, temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { usbd_xfer_set_frame_len(xfer, x, 0); } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; #if USB_HAVE_TT_SUPPORT switch (xfer->error) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: /* nothing to do */ break; default: /* try to reset the TT, if any */ USB_BUS_LOCK(bus); uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint); USB_BUS_UNLOCK(bus); break; } #endif /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on endpoint %p.\n", xfer, xfer->endpoint); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } } ep = xfer->endpoint; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(bus); if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], NULL); if (ep->endpoint_q[xfer->stream_id].curr != NULL || TAILQ_FIRST(&ep->endpoint_q[xfer->stream_id].head) != NULL) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->endpoint->is_synced = 0; } } USB_BUS_UNLOCK(bus); done: return (0); } /*------------------------------------------------------------------------* * usb_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { do { /* set both recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usbd_ctrl_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usbd_ctrl_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->ctrl_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->ctrl_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usbd_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->ctrl_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* * Reset clear stall error counter. */ udev->clear_stall_errors = 0; /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usbd_transfer_setup(udev, &iface_index, udev->ctrl_xfer, usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL, &udev->device_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) { USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check that we have a valid case */ if (udev->flags.usb_mode == USB_MODE_HOST && udev->parent_hub != NULL && udev->bus->methods->clear_stall != NULL && ep->methods != NULL) { (udev->bus->methods->clear_stall) (udev, ep); } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle on the USB device side. *------------------------------------------------------------------------*/ void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usbd_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usbd_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usbd_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usbd_transfer_submit(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } /*------------------------------------------------------------------------* * usbd_transfer_poll * * The following function gets called from the USB keyboard driver and * UMASS when the system has paniced. * * NOTE: It is currently not possible to resume normal operation on * the USB controller which has been polled, due to clearing of the * "up_dsleep" and "up_msleep" flags. *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { struct usb_xfer *xfer; struct usb_xfer_root *xroot; struct usb_device *udev; struct usb_proc_msg *pm; uint16_t n; uint16_t drop_bus; uint16_t drop_xfer; for (n = 0; n != max; n++) { /* Extra checks to avoid panic */ xfer = ppxfer[n]; if (xfer == NULL) continue; /* no USB transfer */ xroot = xfer->xroot; if (xroot == NULL) continue; /* no USB root */ udev = xroot->udev; if (udev == NULL) continue; /* no USB device */ if (udev->bus == NULL) continue; /* no BUS structure */ if (udev->bus->methods == NULL) continue; /* no BUS methods */ if (udev->bus->methods->xfer_poll == NULL) continue; /* no poll method */ /* make sure that the BUS mutex is not locked */ drop_bus = 0; while (mtx_owned(&xroot->udev->bus->bus_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(&xroot->udev->bus->bus_mtx); drop_bus++; } /* make sure that the transfer mutex is not locked */ drop_xfer = 0; while (mtx_owned(xroot->xfer_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(xroot->xfer_mtx); drop_xfer++; } /* Make sure cv_signal() and cv_broadcast() is not called */ USB_BUS_CONTROL_XFER_PROC(udev->bus)->up_msleep = 0; USB_BUS_EXPLORE_PROC(udev->bus)->up_msleep = 0; USB_BUS_GIANT_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_PROC(udev->bus)->up_msleep = 0; /* poll USB hardware */ (udev->bus->methods->xfer_poll) (udev->bus); USB_BUS_LOCK(xroot->bus); /* check for clear stall */ if (udev->ctrl_xfer[1] != NULL) { /* poll clear stall start */ pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); /* poll clear stall done thread */ pm = &udev->ctrl_xfer[1]-> xroot->done_m[0].hdr; (pm->pm_callback) (pm); } /* poll done thread */ pm = &xroot->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xroot->bus); /* restore transfer mutex */ while (drop_xfer--) mtx_lock(xroot->xfer_mtx); /* restore BUS mutex */ while (drop_bus--) mtx_lock(&xroot->udev->bus->bus_mtx); } } static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } void * usbd_xfer_softc(struct usb_xfer *xfer) { return (xfer->priv_sc); } void * usbd_xfer_get_priv(struct usb_xfer *xfer) { return (xfer->priv_fifo); } void usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr) { xfer->priv_fifo = ptr; } uint8_t usbd_xfer_state(struct usb_xfer *xfer) { return (xfer->usb_state); } void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 1; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 1; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 1; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 1; break; } } void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 0; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 0; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 0; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 0; break; } } /* * The following function returns in milliseconds when the isochronous * transfer was completed by the hardware. The returned value wraps * around 65536 milliseconds. */ uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer) { return (xfer->isoc_time_complete); } /* * The following function returns non-zero if the max packet size * field was clamped to a valid value. Else it returns zero. */ uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer) { return (xfer->flags_int.maxp_was_clamped); } Index: head/sys/mips/atheros/ar71xx_ehci.c =================================================================== --- head/sys/mips/atheros/ar71xx_ehci.c (revision 276716) +++ head/sys/mips/atheros/ar71xx_ehci.c (revision 276717) @@ -1,260 +1,261 @@ /*- * Copyright (c) 2008 Sam Leffler. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * AR71XX attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EHCI_HC_DEVSTR "AR71XX Integrated USB 2.0 controller" struct ar71xx_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ }; static device_attach_t ar71xx_ehci_attach; static device_detach_t ar71xx_ehci_detach; bs_r_1_proto(reversed); bs_w_1_proto(reversed); static int ar71xx_ehci_probe(device_t self) { device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_NOWILDCARD); } static int ar71xx_ehci_attach(device_t self) { struct ar71xx_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; int err; int rid; /* initialise some bus fields */ sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_bus.usbrev = USB_REV_2_0; /* NB: hints fix the memory location and irq */ rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } /* * Craft special resource for bus space ops that handle * byte-alignment of non-word addresses. */ sc->sc_io_tag = ar71xx_bus_space_reversed; sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); sprintf(sc->sc_vendor, "Atheros"); err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* * Arrange to force Host mode, select big-endian byte alignment, * and arrange to not terminate reset operations (the adapter * will ignore it if we do but might as well save a reg write). * Also, the controller has an embedded Transaction Translator * which means port speed must be read from the Port Status * register following a port enable. */ sc->sc_flags = EHCI_SCFLG_SETMODE; switch (ar71xx_soc) { case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: case AR71XX_SOC_AR9130: case AR71XX_SOC_AR9132: case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; break; default: /* fallthrough */ break; } /* * ehci_reset() needs the correct offset to access the host controller * registers. The AR724x/AR913x offsets aren't 0. */ sc->sc_offs = EHCI_CAPLENGTH(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); (void) ehci_reset(sc); err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: ar71xx_ehci_detach(self); return (ENXIO); } static int ar71xx_ehci_detach(device_t self) { struct ar71xx_ehci_softc *isc = device_get_softc(self); ehci_softc_t *sc = &isc->base; device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar71xx_ehci_probe), DEVMETHOD(device_attach, ar71xx_ehci_attach), DEVMETHOD(device_detach, ar71xx_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, .size = sizeof(struct ar71xx_ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, nexus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/mips/atheros/ar71xx_ohci.c =================================================================== --- head/sys/mips/atheros/ar71xx_ohci.c (revision 276716) +++ head/sys/mips/atheros/ar71xx_ohci.c (revision 276717) @@ -1,212 +1,213 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ar71xx_ohci_attach(device_t dev); static int ar71xx_ohci_detach(device_t dev); static int ar71xx_ohci_probe(device_t dev); struct ar71xx_ohci_softc { struct ohci_softc sc_ohci; }; static int ar71xx_ohci_probe(device_t dev) { device_set_desc(dev, "AR71XX integrated OHCI controller"); return (BUS_PROBE_DEFAULT); } static int ar71xx_ohci_attach(device_t dev) { struct ar71xx_ohci_softc *sc = device_get_softc(dev); int err; int rid; /* initialise some bus fields */ sc->sc_ohci.sc_bus.parent = dev; sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_ohci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return (ENOMEM); } sc->sc_ohci.sc_dev = dev; rid = 0; sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_ohci.sc_io_res == NULL) { err = ENOMEM; goto error; } sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); rid = 0; sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_ohci.sc_irq_res == NULL) { err = ENOMEM; goto error; } sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (sc->sc_ohci.sc_bus.bdev == NULL) { err = ENOMEM; goto error; } device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); if (err) { err = ENXIO; goto error; } strlcpy(sc->sc_ohci.sc_vendor, "Atheros", sizeof(sc->sc_ohci.sc_vendor)); bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(&sc->sc_ohci); if (!err) err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); if (err) goto error; return (0); error: if (err) { ar71xx_ohci_detach(dev); return (err); } return (err); } static int ar71xx_ohci_detach(device_t dev) { struct ar71xx_ohci_softc *sc = device_get_softc(dev); device_t bdev; if (sc->sc_ohci.sc_bus.bdev) { bdev = sc->sc_ohci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); /* * Put the controller into reset, then disable clocks and do * the MI tear down. We have to disable the clocks/hardware * after we do the rest of the teardown. We also disable the * clocks in the opposite order we acquire them, but that * doesn't seem to be absolutely necessary. We free up the * clocks after we disable them, so the system could, in * theory, reuse them. */ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); if (sc->sc_ohci.sc_intr_hdl) { bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); sc->sc_ohci.sc_intr_hdl = NULL; } if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(&sc->sc_ohci); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); sc->sc_ohci.sc_irq_res = NULL; } if (sc->sc_ohci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_res = NULL; sc->sc_ohci.sc_io_tag = 0; sc->sc_ohci.sc_io_hdl = 0; } usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ar71xx_ohci_probe), DEVMETHOD(device_attach, ar71xx_ohci_attach), DEVMETHOD(device_detach, ar71xx_ohci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_driver = { .name = "ohci", .methods = ohci_methods, .size = sizeof(struct ar71xx_ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, apb, ohci_driver, ohci_devclass, 0, 0); Index: head/sys/mips/cavium/usb/octusb_octeon.c =================================================================== --- head/sys/mips/cavium/usb/octusb_octeon.c (revision 276716) +++ head/sys/mips/cavium/usb/octusb_octeon.c (revision 276717) @@ -1,222 +1,223 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 static device_identify_t octusb_octeon_identify; static device_probe_t octusb_octeon_probe; static device_attach_t octusb_octeon_attach; static device_detach_t octusb_octeon_detach; struct octusb_octeon_softc { struct octusb_softc sc_dci; /* must be first */ }; static void octusb_octeon_identify(driver_t *drv, device_t parent) { if (octeon_has_feature(OCTEON_FEATURE_USB)) BUS_ADD_CHILD(parent, 0, "octusb", 0); } static int octusb_octeon_probe(device_t dev) { device_set_desc(dev, "Cavium Octeon USB controller"); return (0); } static int octusb_octeon_attach(device_t dev) { struct octusb_octeon_softc *sc = device_get_softc(dev); int err; int rid; int nports; int i; /* setup controller interface softc */ /* initialise some bus fields */ sc->sc_dci.sc_bus.parent = dev; sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices; sc->sc_dci.sc_bus.devices_max = OCTUSB_MAX_DEVICES; + sc->sc_dci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } nports = cvmx_usb_get_num_ports(); if (nports > OCTUSB_MAX_PORTS) panic("octusb: too many USB ports %d", nports); for (i = 0; i < nports; i++) { rid = 0; sc->sc_dci.sc_irq_res[i] = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, OCTEON_IRQ_USB0 + i, OCTEON_IRQ_USB0 + i, 1, RF_ACTIVE); if (!(sc->sc_dci.sc_irq_res[i])) { goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res[i], INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl[i]); #else err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res[i], INTR_TYPE_BIO | INTR_MPSAFE, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl[i]); #endif if (err) { sc->sc_dci.sc_intr_hdl[i] = NULL; goto error; } } sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_dci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus); err = octusb_init(&sc->sc_dci); if (!err) { err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev); } if (err) { goto error; } return (0); error: octusb_octeon_detach(dev); return (ENXIO); } static int octusb_octeon_detach(device_t dev) { struct octusb_octeon_softc *sc = device_get_softc(dev); device_t bdev; int err; int nports; int i; if (sc->sc_dci.sc_bus.bdev) { bdev = sc->sc_dci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_dci.sc_irq_res[0] && sc->sc_dci.sc_intr_hdl[0]) /* * only call octusb_octeon_uninit() after octusb_octeon_init() */ octusb_uninit(&sc->sc_dci); nports = cvmx_usb_get_num_ports(); if (nports > OCTUSB_MAX_PORTS) panic("octusb: too many USB ports %d", nports); for (i = 0; i < nports; i++) { if (sc->sc_dci.sc_irq_res[0] && sc->sc_dci.sc_intr_hdl[0]) { err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res[i], sc->sc_dci.sc_intr_hdl[i]); sc->sc_dci.sc_intr_hdl[i] = NULL; } if (sc->sc_dci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_dci.sc_irq_res[i]); sc->sc_dci.sc_irq_res[i] = NULL; } } usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL); return (0); } static device_method_t octusb_octeon_methods[] = { /* Device interface */ DEVMETHOD(device_identify, octusb_octeon_identify), DEVMETHOD(device_probe, octusb_octeon_probe), DEVMETHOD(device_attach, octusb_octeon_attach), DEVMETHOD(device_detach, octusb_octeon_detach), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t octusb_octeon_driver = { .name = "octusb", .methods = octusb_octeon_methods, .size = sizeof(struct octusb_octeon_softc), }; static devclass_t octusb_octeon_devclass; DRIVER_MODULE(octusb, ciu, octusb_octeon_driver, octusb_octeon_devclass, 0, 0); Index: head/sys/mips/rmi/xls_ehci.c =================================================================== --- head/sys/mips/rmi/xls_ehci.c (revision 276716) +++ head/sys/mips/rmi/xls_ehci.c (revision 276717) @@ -1,225 +1,226 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_attach_t ehci_xls_attach; static device_detach_t ehci_xls_detach; static const char *xlr_usb_dev_desc = "RMI XLR USB 2.0 controller"; static const char *xlr_vendor_desc = "RMI Corp"; static int ehci_xls_probe(device_t self) { /* TODO see if usb is enabled on the board */ device_set_desc(self, xlr_usb_dev_desc); return BUS_PROBE_DEFAULT; } static int ehci_xls_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; int rid; sc->sc_bus.parent = self; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return (ENOMEM); } rid = 0; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); printf("IO Resource tag %lx, hdl %lx, size %lx\n", (u_long)sc->sc_io_tag, (u_long)sc->sc_io_hdl, (u_long)sc->sc_io_size); rid = 0; sc->sc_irq_res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, PIC_USB_IRQ, PIC_USB_IRQ, 1, 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) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, xlr_usb_dev_desc); sprintf(sc->sc_vendor, xlr_vendor_desc); err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *) ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } err = ehci_init(sc); if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(self, "USB probe and attach failed err=%d\n", err); goto error; } return (0); error: ehci_xls_detach(self); return (ENXIO); } static int ehci_xls_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { ehci_detach(sc); err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = 0; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; sc->sc_io_tag = 0; sc->sc_io_hdl = 0; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ehci_xls_probe), DEVMETHOD(device_attach, ehci_xls_attach), DEVMETHOD(device_detach, ehci_xls_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_driver = { .name = "ehci", .methods = ehci_methods, .size = sizeof(struct ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, iodi, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); Index: head/sys/mips/rt305x/rt305x_dotg.c =================================================================== --- head/sys/mips/rt305x/rt305x_dotg.c (revision 276716) +++ head/sys/mips/rt305x/rt305x_dotg.c (revision 276717) @@ -1,230 +1,231 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2010,2011 Aleksandr Rybalko. All rights reserved. * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 static device_probe_t dotg_obio_probe; static device_attach_t dotg_obio_attach; static device_detach_t dotg_obio_detach; struct dotg_obio_softc { struct dotg_softc sc_dci; /* must be first */ }; static int dotg_obio_probe(device_t dev) { device_set_desc(dev, "DWC like USB OTG controller"); return (0); } static int dotg_obio_attach(device_t dev) { struct dotg_obio_softc *sc = device_get_softc(dev); int err; /* setup controller interface softc */ /* initialise some bus fields */ sc->sc_dci.sc_dev = dev; sc->sc_dci.sc_bus.parent = dev; sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices; sc->sc_dci.sc_bus.devices_max = DOTG_MAX_DEVICES; + sc->sc_dci.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { printf("No mem\n"); return (ENOMEM); } sc->sc_dci.sc_mem_rid = 0; sc->sc_dci.sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_dci.sc_irq_rid, RF_ACTIVE); if (!(sc->sc_dci.sc_mem_res)) { printf("Can`t alloc MEM\n"); goto error; } sc->sc_dci.sc_bst = rman_get_bustag(sc->sc_dci.sc_mem_res); sc->sc_dci.sc_bsh = rman_get_bushandle(sc->sc_dci.sc_mem_res); sc->sc_dci.sc_irq_rid = 0; sc->sc_dci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_dci.sc_irq_rid, RF_SHAREABLE| RF_ACTIVE); if (!(sc->sc_dci.sc_irq_res)) { printf("Can`t alloc IRQ\n"); goto error; } sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_dci.sc_bus.bdev)) { printf("Can`t add usbus\n"); goto error; } device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus); #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)dotg_interrupt, sc, &sc->sc_dci.sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (driver_intr_t *)dotg_interrupt, sc, &sc->sc_dci.sc_intr_hdl); #endif if (err) { sc->sc_dci.sc_intr_hdl = NULL; printf("Can`t set IRQ handle\n"); goto error; } /* Run clock for OTG core */ rt305x_sysctl_set(SYSCTL_CLKCFG1, rt305x_sysctl_get(SYSCTL_CLKCFG1) | SYSCTL_CLKCFG1_OTG_CLK_EN); rt305x_sysctl_set(SYSCTL_RSTCTRL, SYSCTL_RSTCTRL_OTG); DELAY(100); err = dotg_init(&sc->sc_dci); if (err) printf("dotg_init fail\n"); if (!err) { err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev); if (err) printf("device_probe_and_attach fail\n"); } if (err) { goto error; } return (0); error: dotg_obio_detach(dev); return (ENXIO); } static int dotg_obio_detach(device_t dev) { struct dotg_obio_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_dci.sc_bus.bdev) { bdev = sc->sc_dci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) { /* * only call dotg_obio_uninit() after dotg_obio_init() */ dotg_uninit(&sc->sc_dci); /* Stop OTG clock */ rt305x_sysctl_set(SYSCTL_CLKCFG1, rt305x_sysctl_get(SYSCTL_CLKCFG1) & ~SYSCTL_CLKCFG1_OTG_CLK_EN); err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res, sc->sc_dci.sc_intr_hdl); sc->sc_dci.sc_intr_hdl = NULL; } if (sc->sc_dci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_dci.sc_irq_res); sc->sc_dci.sc_irq_res = NULL; } if (sc->sc_dci.sc_mem_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_dci.sc_mem_res); sc->sc_dci.sc_mem_res = NULL; } usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL); return (0); } static device_method_t dotg_obio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dotg_obio_probe), DEVMETHOD(device_attach, dotg_obio_attach), DEVMETHOD(device_detach, dotg_obio_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t dotg_obio_driver = { .name = "dotg", .methods = dotg_obio_methods, .size = sizeof(struct dotg_obio_softc), }; static devclass_t dotg_obio_devclass; DRIVER_MODULE(dotg, obio, dotg_obio_driver, dotg_obio_devclass, 0, 0); Index: head/sys/powerpc/ps3/ehci_ps3.c =================================================================== --- head/sys/powerpc/ps3/ehci_ps3.c (revision 276716) +++ head/sys/powerpc/ps3/ehci_ps3.c (revision 276717) @@ -1,171 +1,173 @@ /*- * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3bus.h" struct ps3_ehci_softc { ehci_softc_t base; struct bus_space tag; }; static int ehci_ps3_probe(device_t dev) { if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS || ps3bus_get_devtype(dev) != PS3_DEVTYPE_USB) return (ENXIO); device_set_desc(dev, "Playstation 3 USB 2.0 controller"); return (BUS_PROBE_SPECIFIC); } static int ehci_ps3_attach(device_t dev) { ehci_softc_t *sc = device_get_softc(dev); int rid, err; sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; + /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) return (ENOMEM); rid = 1; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(dev, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 1; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Could not allocate irq\n"); return (ENXIO); } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); return (ENXIO); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); sprintf(sc->sc_vendor, "Sony"); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not setup error irq, %d\n", err); goto error; } sc->sc_flags |= EHCI_SCFLG_BIGEMMIO; err = ehci_init(sc); if (err) { device_printf(dev, "USB init failed err=%d\n", err); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err == 0) return (0); error: return (ENXIO); } static device_method_t ehci_ps3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ehci_ps3_probe), DEVMETHOD(device_attach, ehci_ps3_attach), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ehci_ps3_driver = { .name = "ehci", .methods = ehci_ps3_methods, .size = sizeof(ehci_softc_t), }; static devclass_t ehci_ps3_devclass; DRIVER_MODULE(ehci_ps3, ps3bus, ehci_ps3_driver, ehci_ps3_devclass, 0, 0); MODULE_DEPEND(ehci_ps3, usb, 1, 1, 1); Index: head/sys/powerpc/ps3/ohci_ps3.c =================================================================== --- head/sys/powerpc/ps3/ohci_ps3.c (revision 276716) +++ head/sys/powerpc/ps3/ohci_ps3.c (revision 276717) @@ -1,170 +1,172 @@ /*- * Copyright (C) 2010 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3bus.h" static int ohci_ps3_probe(device_t dev) { if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS || ps3bus_get_devtype(dev) != PS3_DEVTYPE_USB) return (ENXIO); device_set_desc(dev, "Playstation 3 USB 2.0 controller"); return (BUS_PROBE_SPECIFIC); } static int ohci_ps3_attach(device_t dev) { ohci_softc_t *sc = device_get_softc(dev); int rid, err; sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = OHCI_MAX_DEVICES; + sc->sc_bus.dma_bits = 32; + /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) return (ENOMEM); rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(dev, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Could not allocate irq\n"); return (ENXIO); } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); return (ENXIO); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); sprintf(sc->sc_vendor, "Sony"); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ohci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not setup error irq, %d\n", err); goto error; } //sc->sc_flags |= EHCI_SCFLG_BIGEMMIO; bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(sc); if (err) { device_printf(dev, "USB init failed err=%d\n", err); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err == 0) return (0); error: return (ENXIO); } static device_method_t ohci_ps3_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_ps3_probe), DEVMETHOD(device_attach, ohci_ps3_attach), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t ohci_ps3_driver = { .name = "ohci", .methods = ohci_ps3_methods, .size = sizeof(ohci_softc_t), }; static devclass_t ohci_ps3_devclass; DRIVER_MODULE(ohci_ps3, ps3bus, ohci_ps3_driver, ohci_ps3_devclass, 0, 0); MODULE_DEPEND(ohci_ps3, usb, 1, 1, 1);