Index: head/sys/arm/allwinner/a10_ehci.c =================================================================== --- head/sys/arm/allwinner/a10_ehci.c (revision 333073) +++ head/sys/arm/allwinner/a10_ehci.c (revision 333074) @@ -1,369 +1,369 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 #include #include #define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" #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 USB_CONF(d) \ (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data #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; struct clk_list { TAILQ_ENTRY(clk_list) next; clk_t clk; }; struct hwrst_list { TAILQ_ENTRY(hwrst_list) next; hwreset_t rst; }; struct aw_ehci_softc { ehci_softc_t sc; TAILQ_HEAD(, clk_list) clk_list; TAILQ_HEAD(, hwrst_list) rst_list; phy_t phy; }; struct aw_ehci_conf { bool sdram_init; }; static const struct aw_ehci_conf a10_ehci_conf = { .sdram_init = true, }; static const struct aw_ehci_conf a31_ehci_conf = { .sdram_init = false, }; static struct ofw_compat_data compat_data[] = { { "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf }, { "allwinner,sun5i-a13-ehci", (uintptr_t)&a10_ehci_conf }, { "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf }, { "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf }, { "allwinner,sun8i-a83t-ehci", (uintptr_t)&a31_ehci_conf }, { "allwinner,sun8i-h3-ehci", (uintptr_t)&a31_ehci_conf }, { "allwinner,sun50i-a64-ehci", (uintptr_t)&a31_ehci_conf }, { NULL, (uintptr_t)NULL } }; static int a10_ehci_probe(device_t self) { if (!ofw_bus_status_okay(self)) return (ENXIO); if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } static int a10_ehci_attach(device_t self) { struct aw_ehci_softc *aw_sc = device_get_softc(self); ehci_softc_t *sc = &aw_sc->sc; const struct aw_ehci_conf *conf; bus_space_handle_t bsh; int err, rid, off; struct clk_list *clkp; clk_t clk; struct hwrst_list *rstp; hwreset_t rst; uint32_t reg_value = 0; conf = USB_CONF(self); /* 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"); 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 */ TAILQ_INIT(&aw_sc->clk_list); for (off = 0; clk_get_by_ofw_index(self, 0, off, &clk) == 0; off++) { err = clk_enable(clk); if (err != 0) { device_printf(self, "Could not enable clock %s\n", clk_get_name(clk)); goto error; } clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO); clkp->clk = clk; TAILQ_INSERT_TAIL(&aw_sc->clk_list, clkp, next); } /* De-assert reset */ TAILQ_INIT(&aw_sc->rst_list); for (off = 0; hwreset_get_by_ofw_idx(self, 0, off, &rst) == 0; off++) { err = hwreset_deassert(rst); if (err != 0) { device_printf(self, "Could not de-assert reset\n"); goto error; } rstp = malloc(sizeof(*rstp), M_DEVBUF, M_WAITOK | M_ZERO); rstp->rst = rst; TAILQ_INSERT_TAIL(&aw_sc->rst_list, rstp, next); } /* Enable USB PHY */ if (phy_get_by_ofw_name(self, 0, "usb", &aw_sc->phy) == 0) { err = phy_enable(aw_sc->phy); if (err != 0) { device_printf(self, "Could not enable phy\n"); goto error; } } /* Configure port */ if (conf->sdram_init) { 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) { struct aw_ehci_softc *aw_sc = device_get_softc(self); ehci_softc_t *sc = &aw_sc->sc; const struct aw_ehci_conf *conf; int err; uint32_t reg_value = 0; struct clk_list *clk, *clk_tmp; struct hwrst_list *rst, *rst_tmp; conf = USB_CONF(self); /* 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 */ if (conf->sdram_init) { 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 clock */ TAILQ_FOREACH_SAFE(clk, &aw_sc->clk_list, next, clk_tmp) { err = clk_disable(clk->clk); if (err != 0) device_printf(self, "Could not disable clock %s\n", clk_get_name(clk->clk)); err = clk_release(clk->clk); if (err != 0) device_printf(self, "Could not release clock %s\n", clk_get_name(clk->clk)); TAILQ_REMOVE(&aw_sc->clk_list, clk, next); free(clk, M_DEVBUF); } /* Assert reset */ TAILQ_FOREACH_SAFE(rst, &aw_sc->rst_list, next, rst_tmp) { hwreset_assert(rst->rst); hwreset_release(rst->rst); TAILQ_REMOVE(&aw_sc->rst_list, rst, next); free(rst, M_DEVBUF); } 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(struct aw_ehci_softc), }; static devclass_t ehci_devclass; -DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); -MODULE_DEPEND(ehci, usb, 1, 1, 1); +DRIVER_MODULE(a10_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(a10_ehci, usb, 1, 1, 1); Index: head/sys/arm/cavium/cns11xx/ehci_ebus.c =================================================================== --- head/sys/arm/cavium/cns11xx/ehci_ebus.c (revision 333073) +++ head/sys/arm/cavium/cns11xx/ehci_ebus.c (revision 333074) @@ -1,248 +1,248 @@ /*- * 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); int err; /* 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); +DRIVER_MODULE(ebus_ehci, econaarm, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ebus_ehci, usb, 1, 1, 1); Index: head/sys/arm/freescale/vybrid/vf_ehci.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_ehci.c (revision 333073) +++ head/sys/arm/freescale/vybrid/vf_ehci.c (revision 333074) @@ -1,434 +1,434 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 "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); +DRIVER_MODULE(vybrid_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(vybrid_ehci, usb, 1, 1, 1); static void vybrid_ehci_post_reset(struct ehci_softc *ehci_softc) { uint32_t usbmode; /* Force HOST mode */ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); usbmode &= ~EHCI_UM_CM; usbmode |= EHCI_UM_CM_HOST; EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); } /* * 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 and callbacks*/ sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; sc->sc_vendor_post_reset = vybrid_ehci_post_reset; sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; 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/samsung/exynos/exynos5_ehci.c =================================================================== --- head/sys/arm/samsung/exynos/exynos5_ehci.c (revision 333073) +++ head/sys/arm/samsung/exynos/exynos5_ehci.c (revision 333074) @@ -1,398 +1,398 @@ /*- * 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. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #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" #endif /* 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(struct exynos_ehci_softc) }; static devclass_t ehci_devclass; -DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); -MODULE_DEPEND(ehci, usb, 1, 1, 1); +DRIVER_MODULE(exynos_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(exynos_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")) != -1) { 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/usb/omap_ehci.c =================================================================== --- head/sys/arm/ti/usb/omap_ehci.c (revision 333073) +++ head/sys/arm/ti/usb/omap_ehci.c (revision 333074) @@ -1,472 +1,472 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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. */ #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 /* EHCI */ #define OMAP_USBHOST_HCCAPBASE 0x0000 #define OMAP_USBHOST_HCSPARAMS 0x0004 #define OMAP_USBHOST_HCCPARAMS 0x0008 #define OMAP_USBHOST_USBCMD 0x0010 #define OMAP_USBHOST_USBSTS 0x0014 #define OMAP_USBHOST_USBINTR 0x0018 #define OMAP_USBHOST_FRINDEX 0x001C #define OMAP_USBHOST_CTRLDSSEGMENT 0x0020 #define OMAP_USBHOST_PERIODICLISTBASE 0x0024 #define OMAP_USBHOST_ASYNCLISTADDR 0x0028 #define OMAP_USBHOST_CONFIGFLAG 0x0050 #define OMAP_USBHOST_PORTSC(i) (0x0054 + (0x04 * (i))) #define OMAP_USBHOST_INSNREG00 0x0090 #define OMAP_USBHOST_INSNREG01 0x0094 #define OMAP_USBHOST_INSNREG02 0x0098 #define OMAP_USBHOST_INSNREG03 0x009C #define OMAP_USBHOST_INSNREG04 0x00A0 #define OMAP_USBHOST_INSNREG05_UTMI 0x00A4 #define OMAP_USBHOST_INSNREG05_ULPI 0x00A4 #define OMAP_USBHOST_INSNREG06 0x00A8 #define OMAP_USBHOST_INSNREG07 0x00AC #define OMAP_USBHOST_INSNREG08 0x00B0 #define OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND (1 << 5) #define OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT 31 #define OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT 24 #define OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT 22 #define OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT 16 #define OMAP_USBHOST_INSNREG05_ULPI_EXTREGADD_SHIFT 8 #define OMAP_USBHOST_INSNREG05_ULPI_WRDATA_SHIFT 0 #define ULPI_FUNC_CTRL_RESET (1 << 5) /*-------------------------------------------------------------------------*/ /* * Macros for Set and Clear * See ULPI 1.1 specification to find the registers with Set and Clear offsets */ #define ULPI_SET(a) (a + 1) #define ULPI_CLR(a) (a + 2) /*-------------------------------------------------------------------------*/ /* * Register Map */ #define ULPI_VENDOR_ID_LOW 0x00 #define ULPI_VENDOR_ID_HIGH 0x01 #define ULPI_PRODUCT_ID_LOW 0x02 #define ULPI_PRODUCT_ID_HIGH 0x03 #define ULPI_FUNC_CTRL 0x04 #define ULPI_IFC_CTRL 0x07 #define ULPI_OTG_CTRL 0x0a #define ULPI_USB_INT_EN_RISE 0x0d #define ULPI_USB_INT_EN_FALL 0x10 #define ULPI_USB_INT_STS 0x13 #define ULPI_USB_INT_LATCH 0x14 #define ULPI_DEBUG 0x15 #define ULPI_SCRATCH 0x16 #define OMAP_EHCI_HC_DEVSTR "TI OMAP USB 2.0 controller" struct omap_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ device_t sc_dev; }; static device_attach_t omap_ehci_attach; static device_detach_t omap_ehci_detach; /** * 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_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) { uint32_t reg = 0; int i; device_t uhh_dev; uhh_dev = device_get_parent(isc->sc_dev); device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); /* 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 */ for (i = 0; i < OMAP_HS_USB_PORTS; i++) { if (omap_usb_port_mode(uhh_dev, i) == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(isc, i); } 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,ehci-omap")) 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); ehci_softc_t *sc = &isc->base; #ifdef SOC_OMAP4 phandle_t root; #endif int err; int rid; #ifdef SOC_OMAP4 /* * If we're running a Pandaboard, run Pandaboard-specific * init code. */ root = OF_finddevice("/"); if (ofw_bus_node_is_compatible(root, "ti,omap4-panda")) pandaboard_usb_hub_init(); #endif /* 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; sprintf(sc->sc_vendor, "Texas Instruments"); /* 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); } /* 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; } /* 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); /* 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; int err; /* 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; } 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, 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} }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(struct omap_ehci_softc), }; static devclass_t ehci_devclass; -DRIVER_MODULE(ehci, omap_uhh, ehci_driver, ehci_devclass, 0, 0); +DRIVER_MODULE(omap_ehci, omap_uhh, ehci_driver, ehci_devclass, 0, 0); Index: head/sys/arm/xilinx/zy7_ehci.c =================================================================== --- head/sys/arm/xilinx/zy7_ehci.c (revision 333073) +++ head/sys/arm/xilinx/zy7_ehci.c (revision 333074) @@ -1,374 +1,374 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 /* 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 void zy7_ehci_post_reset(struct ehci_softc *ehci_softc) { uint32_t usbmode; /* Force HOST mode */ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); usbmode &= ~EHCI_UM_CM; usbmode |= EHCI_UM_CM_HOST; EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); } 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_TT | EHCI_SCFLG_NORESTERM; sc->sc_vendor_post_reset = zy7_ehci_post_reset; sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; /* 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); /* during module unload there are lots of children leftover */ device_delete_children(dev); if ((sc->sc_flags & EHCI_SCFLG_DONEINIT) != 0) { ehci_detach(sc); sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; } 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); +DRIVER_MODULE(zy7_ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL); +MODULE_DEPEND(zy7_ehci, usb, 1, 1, 1);