Index: stable/11/sys/arm/allwinner/a10_ahci.c =================================================================== --- stable/11/sys/arm/allwinner/a10_ahci.c (revision 346523) +++ stable/11/sys/arm/allwinner/a10_ahci.c (revision 346524) @@ -1,395 +1,395 @@ /*- * Copyright (c) 2014-2015 M. Warner Losh * Copyright (c) 2015 Luiz Otavio O Souza * 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. * * The magic-bit-bang sequence used in this code may be based on a linux * platform driver in the Allwinner SDK from Allwinner Technology Co., Ltd. * www.allwinnertech.com, by Daniel Wang * though none of the original code was copied. */ #include "opt_bus.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include /* * Allwinner a1x/a2x/a8x SATA attachment. This is just the AHCI register * set with a few extra implementation-specific registers that need to * be accounted for. There's only one PHY in the system, and it needs * to be trained to bring the link up. In addition, there's some DMA * specific things that need to be done as well. These things are also * just about completely undocumented, except in ugly code in the Linux * SDK Allwinner releases. */ /* BITx -- Unknown bit that needs to be set/cleared at position x */ /* UFx -- Uknown multi-bit field frobbed during init */ #define AHCI_BISTAFR 0x00A0 #define AHCI_BISTCR 0x00A4 #define AHCI_BISTFCTR 0x00A8 #define AHCI_BISTSR 0x00AC #define AHCI_BISTDECR 0x00B0 #define AHCI_DIAGNR 0x00B4 #define AHCI_DIAGNR1 0x00B8 #define AHCI_OOBR 0x00BC #define AHCI_PHYCS0R 0x00C0 /* Bits 0..17 are a mystery */ #define PHYCS0R_BIT18 (1 << 18) #define PHYCS0R_POWER_ENABLE (1 << 19) #define PHYCS0R_UF1_MASK (7 << 20) /* Unknown Field 1 */ #define PHYCS0R_UF1_INIT (3 << 20) #define PHYCS0R_BIT23 (1 << 23) #define PHYCS0R_UF2_MASK (7 << 24) /* Uknown Field 2 */ #define PHYCS0R_UF2_INIT (5 << 24) /* Bit 27 mystery */ #define PHYCS0R_POWER_STATUS_MASK (7 << 28) #define PHYCS0R_PS_GOOD (2 << 28) /* Bit 31 mystery */ #define AHCI_PHYCS1R 0x00C4 /* Bits 0..5 are a mystery */ #define PHYCS1R_UF1_MASK (3 << 6) #define PHYCS1R_UF1_INIT (2 << 6) #define PHYCS1R_UF2_MASK (0x1f << 8) #define PHYCS1R_UF2_INIT (6 << 8) /* Bits 13..14 are a mystery */ #define PHYCS1R_BIT15 (1 << 15) #define PHYCS1R_UF3_MASK (3 << 16) #define PHYCS1R_UF3_INIT (2 << 16) /* Bit 18 mystery */ #define PHYCS1R_HIGHZ (1 << 19) /* Bits 20..27 mystery */ #define PHYCS1R_BIT28 (1 << 28) /* Bits 29..31 mystery */ #define AHCI_PHYCS2R 0x00C8 /* bits 0..4 mystery */ #define PHYCS2R_UF1_MASK (0x1f << 5) #define PHYCS2R_UF1_INIT (0x19 << 5) /* Bits 10..23 mystery */ #define PHYCS2R_CALIBRATE (1 << 24) /* Bits 25..31 mystery */ #define AHCI_TIMER1MS 0x00E0 #define AHCI_GPARAM1R 0x00E8 #define AHCI_GPARAM2R 0x00EC #define AHCI_PPARAMR 0x00F0 #define AHCI_TESTR 0x00F4 #define AHCI_VERSIONR 0x00F8 #define AHCI_IDR 0x00FC #define AHCI_RWCR 0x00FC #define AHCI_P0DMACR 0x0070 #define AHCI_P0PHYCR 0x0078 #define AHCI_P0PHYSR 0x007C #define PLL_FREQ 100000000 static void inline ahci_set(struct resource *m, bus_size_t off, uint32_t set) { uint32_t val = ATA_INL(m, off); val |= set; ATA_OUTL(m, off, val); } static void inline ahci_clr(struct resource *m, bus_size_t off, uint32_t clr) { uint32_t val = ATA_INL(m, off); val &= ~clr; ATA_OUTL(m, off, val); } static void inline ahci_mask_set(struct resource *m, bus_size_t off, uint32_t mask, uint32_t set) { uint32_t val = ATA_INL(m, off); val &= mask; val |= set; ATA_OUTL(m, off, val); } /* * Should this be phy_reset or phy_init */ #define PHY_RESET_TIMEOUT 1000 static void ahci_a10_phy_reset(device_t dev) { uint32_t to, val; struct ahci_controller *ctlr = device_get_softc(dev); /* * Here starts the magic -- most of the comments are based * on guesswork, names of routines and printf error * messages. The code works, but it will do that even if the * comments are 100% BS. */ /* * Lock out other access while we initialize. Or at least that * seems to be the case based on Linux SDK #defines. Maybe this * put things into reset? */ ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 0); DELAY(100); /* * Set bit 19 in PHYCS1R. Guessing this disables driving the PHY * port for a bit while we reset things. */ ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ); /* * Frob PHYCS0R... */ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R, ~PHYCS0R_UF2_MASK, PHYCS0R_UF2_INIT | PHYCS0R_BIT23 | PHYCS0R_BIT18); /* * Set three fields in PHYCS1R */ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS1R, ~(PHYCS1R_UF1_MASK | PHYCS1R_UF2_MASK | PHYCS1R_UF3_MASK), PHYCS1R_UF1_INIT | PHYCS1R_UF2_INIT | PHYCS1R_UF3_INIT); /* * Two more mystery bits in PHYCS1R. -- can these be combined above? */ ahci_set(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_BIT15 | PHYCS1R_BIT28); /* * Now clear that first mysery bit. Perhaps this starts * driving the PHY again so we can power it up and start * talking to the SATA drive, if any below. */ ahci_clr(ctlr->r_mem, AHCI_PHYCS1R, PHYCS1R_HIGHZ); /* * Frob PHYCS0R again... */ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS0R, ~PHYCS0R_UF1_MASK, PHYCS0R_UF1_INIT); /* * Frob PHYCS2R, because 25 means something? */ ahci_mask_set(ctlr->r_mem, AHCI_PHYCS2R, ~PHYCS2R_UF1_MASK, PHYCS2R_UF1_INIT); DELAY(100); /* WAG */ /* * Turn on the power to the PHY and wait for it to report back * good? */ ahci_set(ctlr->r_mem, AHCI_PHYCS0R, PHYCS0R_POWER_ENABLE); for (to = PHY_RESET_TIMEOUT; to > 0; to--) { val = ATA_INL(ctlr->r_mem, AHCI_PHYCS0R); if ((val & PHYCS0R_POWER_STATUS_MASK) == PHYCS0R_PS_GOOD) break; DELAY(10); } if (to == 0 && bootverbose) device_printf(dev, "PHY Power Failed PHYCS0R = %#x\n", val); /* * Calibrate the clocks between the device and the host. This appears * to be an automated process that clears the bit when it is done. */ ahci_set(ctlr->r_mem, AHCI_PHYCS2R, PHYCS2R_CALIBRATE); for (to = PHY_RESET_TIMEOUT; to > 0; to--) { val = ATA_INL(ctlr->r_mem, AHCI_PHYCS2R); if ((val & PHYCS2R_CALIBRATE) == 0) break; DELAY(10); } if (to == 0 && bootverbose) device_printf(dev, "PHY Cal Failed PHYCS2R %#x\n", val); /* * OK, let things settle down a bit. */ DELAY(1000); /* * Go back into normal mode now that we've calibrated the PHY. */ ATA_OUTL(ctlr->r_mem, AHCI_RWCR, 7); } static void ahci_a10_ch_start(struct ahci_channel *ch) { uint32_t reg; /* * Magical values from Allwinner SDK, setup the DMA before start * operations on this channel. */ reg = ATA_INL(ch->r_mem, AHCI_P0DMACR); reg &= ~0xff00; reg |= 0x4400; ATA_OUTL(ch->r_mem, AHCI_P0DMACR, reg); } static int ahci_a10_ctlr_reset(device_t dev) { ahci_a10_phy_reset(dev); return (ahci_ctlr_reset(dev)); } static int ahci_a10_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ahci")) return (ENXIO); device_set_desc(dev, "Allwinner Integrated AHCI controller"); return (BUS_PROBE_DEFAULT); } static int ahci_a10_attach(device_t dev) { int error; struct ahci_controller *ctlr; clk_t clk_pll, clk_gate; ctlr = device_get_softc(dev); clk_pll = clk_gate = NULL; ctlr->quirks = AHCI_Q_NOPMP; ctlr->vendorid = 0; ctlr->deviceid = 0; ctlr->subvendorid = 0; ctlr->subdeviceid = 0; ctlr->r_rid = 0; if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE))) return (ENXIO); /* Enable clocks */ error = clk_get_by_ofw_index(dev, 0, 0, &clk_pll); if (error != 0) { device_printf(dev, "Cannot get PLL clock\n"); goto fail; } error = clk_get_by_ofw_index(dev, 0, 1, &clk_gate); if (error != 0) { device_printf(dev, "Cannot get gate clock\n"); goto fail; } error = clk_set_freq(clk_pll, PLL_FREQ, CLK_SET_ROUND_DOWN); if (error != 0) { device_printf(dev, "Cannot set PLL frequency\n"); goto fail; } error = clk_enable(clk_pll); if (error != 0) { device_printf(dev, "Cannot enable PLL\n"); goto fail; } error = clk_enable(clk_gate); if (error != 0) { device_printf(dev, "Cannot enable clk gate\n"); goto fail; } /* Reset controller */ if ((error = ahci_a10_ctlr_reset(dev)) != 0) goto fail; /* * No MSI registers on this platform. */ ctlr->msi = 0; ctlr->numirqs = 1; /* Channel start callback(). */ ctlr->ch_start = ahci_a10_ch_start; /* * Note: ahci_attach will release ctlr->r_mem on errors automatically */ return (ahci_attach(dev)); fail: if (clk_gate != NULL) clk_release(clk_gate); if (clk_pll != NULL) clk_release(clk_pll); bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (error); } static int ahci_a10_detach(device_t dev) { return (ahci_detach(dev)); } static device_method_t ahci_ata_methods[] = { DEVMETHOD(device_probe, ahci_a10_probe), DEVMETHOD(device_attach, ahci_a10_attach), DEVMETHOD(device_detach, ahci_a10_detach), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), DEVMETHOD_END }; static driver_t ahci_ata_driver = { "ahci", ahci_ata_methods, sizeof(struct ahci_controller) }; -DRIVER_MODULE(ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0); +DRIVER_MODULE(a10_ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0); Index: stable/11/sys/arm/allwinner/a10_ehci.c =================================================================== --- stable/11/sys/arm/allwinner/a10_ehci.c (revision 346523) +++ stable/11/sys/arm/allwinner/a10_ehci.c (revision 346524) @@ -1,365 +1,365 @@ /*- * 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_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 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 aw_ehci_softc { ehci_softc_t sc; clk_t clk; hwreset_t rst; 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 }, { 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; int rid; 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; /* De-assert reset */ if (hwreset_get_by_ofw_idx(self, 0, 0, &aw_sc->rst) == 0) { err = hwreset_deassert(aw_sc->rst); if (err != 0) { device_printf(self, "Could not de-assert reset\n"); goto error; } } /* Enable clock for USB */ err = clk_get_by_ofw_index(self, 0, 0, &aw_sc->clk); if (err != 0) { device_printf(self, "Could not get clock\n"); goto error; } err = clk_enable(aw_sc->clk); if (err != 0) { device_printf(self, "Could not enable clock\n"); goto error; } /* Enable USB PHY */ err = phy_get_by_ofw_name(self, 0, "usb", &aw_sc->phy); if (err != 0) { device_printf(self, "Could not get phy\n"); goto error; } err = phy_enable(aw_sc->phy); if (err != 0) { device_printf(self, "Could not enable phy\n"); goto error; } /* 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 */ 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: if (aw_sc->clk != NULL) { clk_disable(aw_sc->clk); clk_release(aw_sc->clk); } 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; 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 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 */ if (aw_sc->clk != NULL) { clk_disable(aw_sc->clk); clk_release(aw_sc->clk); } /* Assert reset */ if (aw_sc->rst != NULL) { hwreset_assert(aw_sc->rst); hwreset_release(aw_sc->rst); } 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: stable/11/sys/arm/cavium/cns11xx/ehci_ebus.c =================================================================== --- stable/11/sys/arm/cavium/cns11xx/ehci_ebus.c (revision 346523) +++ stable/11/sys/arm/cavium/cns11xx/ehci_ebus.c (revision 346524) @@ -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: stable/11/sys/arm/freescale/imx/imx6_ahci.c =================================================================== --- stable/11/sys/arm/freescale/imx/imx6_ahci.c (revision 346523) +++ stable/11/sys/arm/freescale/imx/imx6_ahci.c (revision 346524) @@ -1,356 +1,356 @@ /*- * Copyright (c) 2017 Rogiel Sulzbach * 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 #define SATA_TIMER1MS 0x000000e0 #define SATA_P0PHYCR 0x00000178 #define SATA_P0PHYCR_CR_READ (1 << 19) #define SATA_P0PHYCR_CR_WRITE (1 << 18) #define SATA_P0PHYCR_CR_CAP_DATA (1 << 17) #define SATA_P0PHYCR_CR_CAP_ADDR (1 << 16) #define SATA_P0PHYCR_CR_DATA_IN(v) ((v) & 0xffff) #define SATA_P0PHYSR 0x0000017c #define SATA_P0PHYSR_CR_ACK (1 << 18) #define SATA_P0PHYSR_CR_DATA_OUT(v) ((v) & 0xffff) /* phy registers */ #define SATA_PHY_CLOCK_RESET 0x7f3f #define SATA_PHY_CLOCK_RESET_RST (1 << 0) #define SATA_PHY_LANE0_OUT_STAT 0x2003 #define SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE (1 << 1) static int imx6_ahci_phy_ctrl(struct ahci_controller* sc, uint32_t bitmask, bool on) { uint32_t v; int timeout; bool state; v = ATA_INL(sc->r_mem, SATA_P0PHYCR); if (on) { v |= bitmask; } else { v &= ~bitmask; } ATA_OUTL(sc->r_mem, SATA_P0PHYCR, v); for (timeout = 5000; timeout > 0; --timeout) { v = ATA_INL(sc->r_mem, SATA_P0PHYSR); state = (v & SATA_P0PHYSR_CR_ACK) == SATA_P0PHYSR_CR_ACK; if(state == on) { break; } DELAY(100); } if (timeout > 0) { return (0); } return (ETIMEDOUT); } static int imx6_ahci_phy_addr(struct ahci_controller* sc, uint32_t addr) { int error; DELAY(100); ATA_OUTL(sc->r_mem, SATA_P0PHYCR, addr); error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, true); if (error != 0) { device_printf(sc->dev, "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=1\n", __FUNCTION__); return (error); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, false); if (error != 0) { device_printf(sc->dev, "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=0\n", __FUNCTION__); return (error); } return (0); } static int imx6_ahci_phy_write(struct ahci_controller* sc, uint32_t addr, uint16_t data) { int error; error = imx6_ahci_phy_addr(sc, addr); if (error != 0) { device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n", __FUNCTION__); return (error); } ATA_OUTL(sc->r_mem, SATA_P0PHYCR, data); error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, true); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_CAP_DATA=1\n", __FUNCTION__); return (error); } if (imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, false) != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_CAP_DATA=0\n", __FUNCTION__); return (error); } if ((addr == SATA_PHY_CLOCK_RESET) && data) { /* we can't check ACK after RESET */ ATA_OUTL(sc->r_mem, SATA_P0PHYCR, SATA_P0PHYCR_CR_DATA_IN(data) | SATA_P0PHYCR_CR_WRITE); return (0); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, true); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=1\n", __FUNCTION__); return (error); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, false); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=0\n", __FUNCTION__); return (error); } return (0); } static int imx6_ahci_phy_read(struct ahci_controller* sc, uint32_t addr, uint16_t* val) { int error; uint32_t v; error = imx6_ahci_phy_addr(sc, addr); if (error != 0) { device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n", __FUNCTION__); return (error); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, true); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=1\n", __FUNCTION__); return (error); } v = ATA_INL(sc->r_mem, SATA_P0PHYSR); error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, false); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=0\n", __FUNCTION__); return (error); } *val = SATA_P0PHYSR_CR_DATA_OUT(v); return (0); } static int imx6_ahci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) { return (ENXIO); } if (!ofw_bus_is_compatible(dev, "fsl,imx6q-ahci")) { return (ENXIO); } device_set_desc(dev, "i.MX6 Integrated AHCI controller"); return (BUS_PROBE_DEFAULT); } static int imx6_ahci_attach(device_t dev) { struct ahci_controller* ctlr; uint16_t pllstat; uint32_t v; int error, timeout; ctlr = device_get_softc(dev); /* Power up the controller and phy. */ error = imx6_ccm_sata_enable(); if (error != 0) { device_printf(dev, "error enabling controller and phy\n"); return (error); } ctlr->vendorid = 0; ctlr->deviceid = 0; ctlr->subvendorid = 0; ctlr->subdeviceid = 0; ctlr->numirqs = 1; ctlr->r_rid = 0; if ((ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE)) == NULL) { return (ENXIO); } v = imx_iomux_gpr_get(IOMUX_GPR13); /* Clear out existing values; these numbers are bitmasks. */ v &= ~(IOMUX_GPR13_SATA_PHY_8(7) | IOMUX_GPR13_SATA_PHY_7(0x1f) | IOMUX_GPR13_SATA_PHY_6(7) | IOMUX_GPR13_SATA_SPEED(1) | IOMUX_GPR13_SATA_PHY_5(1) | IOMUX_GPR13_SATA_PHY_4(7) | IOMUX_GPR13_SATA_PHY_3(0xf) | IOMUX_GPR13_SATA_PHY_2(0x1f) | IOMUX_GPR13_SATA_PHY_1(1) | IOMUX_GPR13_SATA_PHY_0(1)); /* setting */ v |= IOMUX_GPR13_SATA_PHY_8(5) | /* Rx 3.0db */ IOMUX_GPR13_SATA_PHY_7(0x12) | /* Rx SATA2m */ IOMUX_GPR13_SATA_PHY_6(3) | /* Rx DPLL mode */ IOMUX_GPR13_SATA_SPEED(1) | /* 3.0GHz */ IOMUX_GPR13_SATA_PHY_5(0) | /* SpreadSpectram */ IOMUX_GPR13_SATA_PHY_4(4) | /* Tx Attenuation 9/16 */ IOMUX_GPR13_SATA_PHY_3(0) | /* Tx Boost 0db */ IOMUX_GPR13_SATA_PHY_2(0x11) | /* Tx Level 1.104V */ IOMUX_GPR13_SATA_PHY_1(1); /* PLL clock enable */ imx_iomux_gpr_set(IOMUX_GPR13, v); /* phy reset */ error = imx6_ahci_phy_write(ctlr, SATA_PHY_CLOCK_RESET, SATA_PHY_CLOCK_RESET_RST); if (error != 0) { device_printf(dev, "cannot reset PHY\n"); goto fail; } for (timeout = 50; timeout > 0; --timeout) { DELAY(100); error = imx6_ahci_phy_read(ctlr, SATA_PHY_LANE0_OUT_STAT, &pllstat); if (error != 0) { device_printf(dev, "cannot read LANE0 status\n"); goto fail; } if (pllstat & SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE) { break; } } if (timeout <= 0) { device_printf(dev, "time out reading LANE0 status\n"); error = ETIMEDOUT; goto fail; } /* Support Staggered Spin-up */ v = ATA_INL(ctlr->r_mem, AHCI_CAP); ATA_OUTL(ctlr->r_mem, AHCI_CAP, v | AHCI_CAP_SSS); /* Ports Implemented. must set 1 */ v = ATA_INL(ctlr->r_mem, AHCI_PI); ATA_OUTL(ctlr->r_mem, AHCI_PI, v | (1 << 0)); /* set 1ms-timer = AHB clock / 1000 */ ATA_OUTL(ctlr->r_mem, SATA_TIMER1MS, imx_ccm_ahb_hz() / 1000); /* * Note: ahci_attach will release ctlr->r_mem on errors automatically */ return (ahci_attach(dev)); fail: bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (error); } static int imx6_ahci_detach(device_t dev) { return (ahci_detach(dev)); } static device_method_t imx6_ahci_ata_methods[] = { /* device probe, attach and detach methods */ DEVMETHOD(device_probe, imx6_ahci_probe), DEVMETHOD(device_attach, imx6_ahci_attach), DEVMETHOD(device_detach, imx6_ahci_detach), /* ahci bus methods */ DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr, ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), DEVMETHOD_END }; static driver_t ahci_ata_driver = { "ahci", imx6_ahci_ata_methods, sizeof(struct ahci_controller) }; -DRIVER_MODULE(ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0); +DRIVER_MODULE(imx6_ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0); Index: stable/11/sys/arm/freescale/vybrid/vf_ehci.c =================================================================== --- stable/11/sys/arm/freescale/vybrid/vf_ehci.c (revision 346523) +++ stable/11/sys/arm/freescale/vybrid/vf_ehci.c (revision 346524) @@ -1,429 +1,429 @@ /*- * 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); +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; /* First detach all children; we can't detach if that fails. */ if ((err = device_delete_children(dev)) != 0) return (err); /* * 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; } usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); bus_release_resources(dev, vybrid_ehci_spec, esc->res); return (0); } Index: stable/11/sys/arm/samsung/exynos/exynos5_ehci.c =================================================================== --- stable/11/sys/arm/samsung/exynos/exynos5_ehci.c (revision 346523) +++ stable/11/sys/arm/samsung/exynos/exynos5_ehci.c (revision 346524) @@ -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")) != 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: stable/11/sys/arm/ti/usb/omap_ehci.c =================================================================== --- stable/11/sys/arm/ti/usb/omap_ehci.c (revision 346523) +++ stable/11/sys/arm/ti/usb/omap_ehci.c (revision 346524) @@ -1,456 +1,456 @@ /*- * 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 /* 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; 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 = 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: stable/11/sys/arm/xilinx/zy7_ehci.c =================================================================== --- stable/11/sys/arm/xilinx/zy7_ehci.c (revision 346523) +++ stable/11/sys/arm/xilinx/zy7_ehci.c (revision 346524) @@ -1,376 +1,376 @@ /*- * 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 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); sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; 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_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); Index: stable/11 =================================================================== --- stable/11 (revision 346523) +++ stable/11 (revision 346524) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r333073-333074