1ef2ee5d0SMichal Meloun /*- 2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3ef2ee5d0SMichal Meloun * All rights reserved. 4ef2ee5d0SMichal Meloun * 5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without 6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions 7ef2ee5d0SMichal Meloun * are met: 8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer. 10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution. 13ef2ee5d0SMichal Meloun * 14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ef2ee5d0SMichal Meloun * SUCH DAMAGE. 25ef2ee5d0SMichal Meloun */ 26ef2ee5d0SMichal Meloun 27ef2ee5d0SMichal Meloun #include <sys/cdefs.h> 28ef2ee5d0SMichal Meloun /* 29ef2ee5d0SMichal Meloun * EHCI driver for Tegra SoCs. 30ef2ee5d0SMichal Meloun */ 31ef2ee5d0SMichal Meloun #include "opt_bus.h" 32ef2ee5d0SMichal Meloun #include "opt_platform.h" 33ef2ee5d0SMichal Meloun 34ef2ee5d0SMichal Meloun #include <sys/param.h> 35ef2ee5d0SMichal Meloun #include <sys/systm.h> 36ef2ee5d0SMichal Meloun #include <sys/kernel.h> 37ef2ee5d0SMichal Meloun #include <sys/module.h> 38ef2ee5d0SMichal Meloun #include <sys/bus.h> 39ef2ee5d0SMichal Meloun #include <sys/condvar.h> 40ef2ee5d0SMichal Meloun #include <sys/rman.h> 41ef2ee5d0SMichal Meloun 42ef2ee5d0SMichal Meloun #include <machine/bus.h> 43ef2ee5d0SMichal Meloun #include <machine/resource.h> 44ef2ee5d0SMichal Meloun 45be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 461f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h> 47950a6087SEmmanuel Vadot #include <dev/phy/phy.h> 48ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h> 49ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 50ef2ee5d0SMichal Meloun #include <dev/usb/usb.h> 51ef2ee5d0SMichal Meloun #include <dev/usb/usbdi.h> 52ef2ee5d0SMichal Meloun #include <dev/usb/usb_busdma.h> 53ef2ee5d0SMichal Meloun #include <dev/usb/usb_process.h> 54ef2ee5d0SMichal Meloun #include <dev/usb/usb_controller.h> 55ef2ee5d0SMichal Meloun #include <dev/usb/usb_bus.h> 56ef2ee5d0SMichal Meloun #include <dev/usb/controller/ehci.h> 57ef2ee5d0SMichal Meloun #include <dev/usb/controller/ehcireg.h> 58ef2ee5d0SMichal Meloun 59ef2ee5d0SMichal Meloun #include "usbdevs.h" 60ef2ee5d0SMichal Meloun 61ef2ee5d0SMichal Meloun #define TEGRA_EHCI_REG_OFF 0x100 62ef2ee5d0SMichal Meloun #define TEGRA_EHCI_REG_SIZE 0x100 63ef2ee5d0SMichal Meloun 64ef2ee5d0SMichal Meloun /* Compatible devices. */ 65ef2ee5d0SMichal Meloun #define TEGRA124_EHCI 1 66b9cbd68dSMichal Meloun #define TEGRA210_EHCI 2 67ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = { 68ef2ee5d0SMichal Meloun {"nvidia,tegra124-ehci", (uintptr_t)TEGRA124_EHCI}, 69b9cbd68dSMichal Meloun {"nvidia,tegra210-ehci", (uintptr_t)TEGRA210_EHCI}, 70ef2ee5d0SMichal Meloun {NULL, 0}, 71ef2ee5d0SMichal Meloun }; 72ef2ee5d0SMichal Meloun 73ef2ee5d0SMichal Meloun struct tegra_ehci_softc { 74ef2ee5d0SMichal Meloun ehci_softc_t ehci_softc; 75ef2ee5d0SMichal Meloun device_t dev; 76ef2ee5d0SMichal Meloun struct resource *ehci_mem_res; /* EHCI core regs. */ 77ef2ee5d0SMichal Meloun struct resource *ehci_irq_res; /* EHCI core IRQ. */ 78ef2ee5d0SMichal Meloun int usb_alloc_called; 79ef2ee5d0SMichal Meloun clk_t clk; 80ef2ee5d0SMichal Meloun phy_t phy; 81ef2ee5d0SMichal Meloun hwreset_t reset; 82ef2ee5d0SMichal Meloun }; 83ef2ee5d0SMichal Meloun 84ef2ee5d0SMichal Meloun static void 85ef2ee5d0SMichal Meloun tegra_ehci_post_reset(struct ehci_softc *ehci_softc) 86ef2ee5d0SMichal Meloun { 87ef2ee5d0SMichal Meloun uint32_t usbmode; 88ef2ee5d0SMichal Meloun 89ef2ee5d0SMichal Meloun /* Force HOST mode. */ 90ef2ee5d0SMichal Meloun usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_LPM); 91ef2ee5d0SMichal Meloun usbmode &= ~EHCI_UM_CM; 92ef2ee5d0SMichal Meloun usbmode |= EHCI_UM_CM_HOST; 93ef2ee5d0SMichal Meloun device_printf(ehci_softc->sc_bus.bdev, "set host controller mode\n"); 94ef2ee5d0SMichal Meloun EOWRITE4(ehci_softc, EHCI_USBMODE_LPM, usbmode); 95ef2ee5d0SMichal Meloun } 96ef2ee5d0SMichal Meloun 97ef2ee5d0SMichal Meloun static int 98ef2ee5d0SMichal Meloun tegra_ehci_probe(device_t dev) 99ef2ee5d0SMichal Meloun { 100ef2ee5d0SMichal Meloun 101ef2ee5d0SMichal Meloun if (!ofw_bus_status_okay(dev)) 102ef2ee5d0SMichal Meloun return (ENXIO); 103ef2ee5d0SMichal Meloun 104ef2ee5d0SMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { 105ef2ee5d0SMichal Meloun device_set_desc(dev, "Nvidia Tegra EHCI controller"); 106ef2ee5d0SMichal Meloun return (BUS_PROBE_DEFAULT); 107ef2ee5d0SMichal Meloun } 108ef2ee5d0SMichal Meloun return (ENXIO); 109ef2ee5d0SMichal Meloun } 110ef2ee5d0SMichal Meloun 111ef2ee5d0SMichal Meloun static int 112ef2ee5d0SMichal Meloun tegra_ehci_detach(device_t dev) 113ef2ee5d0SMichal Meloun { 114ef2ee5d0SMichal Meloun struct tegra_ehci_softc *sc; 115ef2ee5d0SMichal Meloun ehci_softc_t *esc; 116*3ddaf820SJohn Baldwin int error; 117*3ddaf820SJohn Baldwin 118*3ddaf820SJohn Baldwin error = bus_generic_detach(dev); 119*3ddaf820SJohn Baldwin if (error != 0) 120*3ddaf820SJohn Baldwin return (error); 121ef2ee5d0SMichal Meloun 122ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 123ef2ee5d0SMichal Meloun 124ef2ee5d0SMichal Meloun esc = &sc->ehci_softc; 125ef2ee5d0SMichal Meloun if (sc->clk != NULL) 126ef2ee5d0SMichal Meloun clk_release(sc->clk); 127ef2ee5d0SMichal Meloun if (esc->sc_flags & EHCI_SCFLG_DONEINIT) 128ef2ee5d0SMichal Meloun ehci_detach(esc); 129ef2ee5d0SMichal Meloun if (esc->sc_intr_hdl != NULL) 130ef2ee5d0SMichal Meloun bus_teardown_intr(dev, esc->sc_irq_res, 131ef2ee5d0SMichal Meloun esc->sc_intr_hdl); 132ef2ee5d0SMichal Meloun if (sc->ehci_irq_res != NULL) 133ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, 134ef2ee5d0SMichal Meloun sc->ehci_irq_res); 135ef2ee5d0SMichal Meloun if (sc->ehci_mem_res != NULL) 136ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, 137ef2ee5d0SMichal Meloun sc->ehci_mem_res); 138ef2ee5d0SMichal Meloun if (sc->usb_alloc_called) 139ef2ee5d0SMichal Meloun usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc); 140ef2ee5d0SMichal Meloun 141ef2ee5d0SMichal Meloun return (0); 142ef2ee5d0SMichal Meloun } 143ef2ee5d0SMichal Meloun 144ef2ee5d0SMichal Meloun static int 145ef2ee5d0SMichal Meloun tegra_ehci_attach(device_t dev) 146ef2ee5d0SMichal Meloun { 147ef2ee5d0SMichal Meloun struct tegra_ehci_softc *sc; 148ef2ee5d0SMichal Meloun ehci_softc_t *esc; 149ef2ee5d0SMichal Meloun int rv, rid; 150ef2ee5d0SMichal Meloun uint64_t freq; 151ef2ee5d0SMichal Meloun 152ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 153ef2ee5d0SMichal Meloun sc->dev = dev; 154ef2ee5d0SMichal Meloun esc = &sc->ehci_softc; 155ef2ee5d0SMichal Meloun 156ef2ee5d0SMichal Meloun /* Allocate resources. */ 157ef2ee5d0SMichal Meloun rid = 0; 158ef2ee5d0SMichal Meloun sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 159ef2ee5d0SMichal Meloun RF_ACTIVE | RF_SHAREABLE); 160ef2ee5d0SMichal Meloun if (sc->ehci_mem_res == NULL) { 161ef2ee5d0SMichal Meloun device_printf(dev, "Cannot allocate memory resources\n"); 162ef2ee5d0SMichal Meloun rv = ENXIO; 163ef2ee5d0SMichal Meloun goto out; 164ef2ee5d0SMichal Meloun } 165ef2ee5d0SMichal Meloun 166ef2ee5d0SMichal Meloun rid = 0; 167ef2ee5d0SMichal Meloun sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 168ef2ee5d0SMichal Meloun RF_ACTIVE); 169ef2ee5d0SMichal Meloun if (sc->ehci_irq_res == NULL) { 170ef2ee5d0SMichal Meloun device_printf(dev, "Cannot allocate IRQ resources\n"); 171ef2ee5d0SMichal Meloun rv = ENXIO; 172ef2ee5d0SMichal Meloun goto out; 173ef2ee5d0SMichal Meloun } 174ef2ee5d0SMichal Meloun 175dac93553SMichal Meloun rv = hwreset_get_by_ofw_name(dev, 0, "usb", &sc->reset); 176ef2ee5d0SMichal Meloun if (rv != 0) { 177ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get reset\n"); 178ef2ee5d0SMichal Meloun rv = ENXIO; 179ef2ee5d0SMichal Meloun goto out; 180ef2ee5d0SMichal Meloun } 181ef2ee5d0SMichal Meloun 182dac93553SMichal Meloun rv = phy_get_by_ofw_property(sc->dev, 0, "nvidia,phy", &sc->phy); 183ef2ee5d0SMichal Meloun if (rv != 0) { 184ef2ee5d0SMichal Meloun device_printf(sc->dev, "Cannot get 'nvidia,phy' phy\n"); 185ef2ee5d0SMichal Meloun rv = ENXIO; 186ef2ee5d0SMichal Meloun goto out; 187ef2ee5d0SMichal Meloun } 188ef2ee5d0SMichal Meloun 189dac93553SMichal Meloun rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk); 190ef2ee5d0SMichal Meloun if (rv != 0) { 191ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get clock\n"); 192ef2ee5d0SMichal Meloun goto out; 193ef2ee5d0SMichal Meloun } 194ef2ee5d0SMichal Meloun 195ef2ee5d0SMichal Meloun rv = clk_enable(sc->clk); 196ef2ee5d0SMichal Meloun if (rv != 0) { 197ef2ee5d0SMichal Meloun device_printf(dev, "Cannot enable clock\n"); 198ef2ee5d0SMichal Meloun goto out; 199ef2ee5d0SMichal Meloun } 200ef2ee5d0SMichal Meloun 201ef2ee5d0SMichal Meloun freq = 0; 202ef2ee5d0SMichal Meloun rv = clk_get_freq(sc->clk, &freq); 203ef2ee5d0SMichal Meloun if (rv != 0) { 204ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get clock frequency\n"); 205ef2ee5d0SMichal Meloun goto out; 206ef2ee5d0SMichal Meloun } 207ef2ee5d0SMichal Meloun 208ef2ee5d0SMichal Meloun rv = hwreset_deassert(sc->reset); 209ef2ee5d0SMichal Meloun if (rv != 0) { 210ef2ee5d0SMichal Meloun device_printf(dev, "Cannot clear reset: %d\n", rv); 211ef2ee5d0SMichal Meloun rv = ENXIO; 212ef2ee5d0SMichal Meloun goto out; 213ef2ee5d0SMichal Meloun } 214ef2ee5d0SMichal Meloun 215f8759facSMichal Meloun rv = phy_enable(sc->phy); 216ef2ee5d0SMichal Meloun if (rv != 0) { 217ef2ee5d0SMichal Meloun device_printf(dev, "Cannot enable phy: %d\n", rv); 218ef2ee5d0SMichal Meloun goto out; 219ef2ee5d0SMichal Meloun } 220ef2ee5d0SMichal Meloun 221ef2ee5d0SMichal Meloun /* Fill data for EHCI driver. */ 222ef2ee5d0SMichal Meloun esc->sc_vendor_get_port_speed = ehci_get_port_speed_hostc; 223ef2ee5d0SMichal Meloun esc->sc_vendor_post_reset = tegra_ehci_post_reset; 224ef2ee5d0SMichal Meloun esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res); 225ef2ee5d0SMichal Meloun esc->sc_bus.parent = dev; 226ef2ee5d0SMichal Meloun esc->sc_bus.devices = esc->sc_devices; 227ef2ee5d0SMichal Meloun esc->sc_bus.devices_max = EHCI_MAX_DEVICES; 228ef2ee5d0SMichal Meloun esc->sc_bus.dma_bits = 32; 229ef2ee5d0SMichal Meloun 230ef2ee5d0SMichal Meloun /* Allocate all DMA memory. */ 231ef2ee5d0SMichal Meloun rv = usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev), 232ef2ee5d0SMichal Meloun &ehci_iterate_hw_softc); 233ef2ee5d0SMichal Meloun sc->usb_alloc_called = 1; 234ef2ee5d0SMichal Meloun if (rv != 0) { 235ef2ee5d0SMichal Meloun device_printf(dev, "usb_bus_mem_alloc_all() failed\n"); 236ef2ee5d0SMichal Meloun rv = ENOMEM; 237ef2ee5d0SMichal Meloun goto out; 238ef2ee5d0SMichal Meloun } 239ef2ee5d0SMichal Meloun 240ef2ee5d0SMichal Meloun /* 241ef2ee5d0SMichal Meloun * Set handle to USB related registers subregion used by 242ef2ee5d0SMichal Meloun * generic EHCI driver. 243ef2ee5d0SMichal Meloun */ 244ef2ee5d0SMichal Meloun rv = bus_space_subregion(esc->sc_io_tag, 245ef2ee5d0SMichal Meloun rman_get_bushandle(sc->ehci_mem_res), 246ef2ee5d0SMichal Meloun TEGRA_EHCI_REG_OFF, TEGRA_EHCI_REG_SIZE, &esc->sc_io_hdl); 247ef2ee5d0SMichal Meloun if (rv != 0) { 248ef2ee5d0SMichal Meloun device_printf(dev, "Could not create USB memory subregion\n"); 249ef2ee5d0SMichal Meloun rv = ENXIO; 250ef2ee5d0SMichal Meloun goto out; 251ef2ee5d0SMichal Meloun } 252ef2ee5d0SMichal Meloun 253ef2ee5d0SMichal Meloun /* Setup interrupt handler. */ 254c520cb4fSMichal Meloun rv = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 255c520cb4fSMichal Meloun NULL, (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl); 256ef2ee5d0SMichal Meloun if (rv != 0) { 257ef2ee5d0SMichal Meloun device_printf(dev, "Could not setup IRQ\n"); 258ef2ee5d0SMichal Meloun goto out; 259ef2ee5d0SMichal Meloun } 260ef2ee5d0SMichal Meloun 261ef2ee5d0SMichal Meloun /* Add USB bus device. */ 2625b56413dSWarner Losh esc->sc_bus.bdev = device_add_child(dev, "usbus", DEVICE_UNIT_ANY); 263ef2ee5d0SMichal Meloun if (esc->sc_bus.bdev == NULL) { 264ef2ee5d0SMichal Meloun device_printf(dev, "Could not add USB device\n"); 265ef2ee5d0SMichal Meloun goto out; 266ef2ee5d0SMichal Meloun } 267ef2ee5d0SMichal Meloun device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus); 268ef2ee5d0SMichal Meloun 269ef2ee5d0SMichal Meloun esc->sc_id_vendor = USB_VENDOR_FREESCALE; 270ef2ee5d0SMichal Meloun strlcpy(esc->sc_vendor, "Nvidia", sizeof(esc->sc_vendor)); 271ef2ee5d0SMichal Meloun 272ef2ee5d0SMichal Meloun /* Set flags that affect ehci_init() behavior. */ 273ef2ee5d0SMichal Meloun esc->sc_flags |= EHCI_SCFLG_TT; 274ef2ee5d0SMichal Meloun esc->sc_flags |= EHCI_SCFLG_NORESTERM; 275ef2ee5d0SMichal Meloun rv = ehci_init(esc); 276ef2ee5d0SMichal Meloun if (rv != 0) { 277ef2ee5d0SMichal Meloun device_printf(dev, "USB init failed: %d\n", 278ef2ee5d0SMichal Meloun rv); 279ef2ee5d0SMichal Meloun goto out; 280ef2ee5d0SMichal Meloun } 281ef2ee5d0SMichal Meloun esc->sc_flags |= EHCI_SCFLG_DONEINIT; 282ef2ee5d0SMichal Meloun 283ef2ee5d0SMichal Meloun /* Probe the bus. */ 284ef2ee5d0SMichal Meloun rv = device_probe_and_attach(esc->sc_bus.bdev); 285ef2ee5d0SMichal Meloun if (rv != 0) { 286ef2ee5d0SMichal Meloun device_printf(dev, 287ef2ee5d0SMichal Meloun "device_probe_and_attach() failed\n"); 288ef2ee5d0SMichal Meloun goto out; 289ef2ee5d0SMichal Meloun } 290ef2ee5d0SMichal Meloun return (0); 291ef2ee5d0SMichal Meloun 292ef2ee5d0SMichal Meloun out: 293ef2ee5d0SMichal Meloun tegra_ehci_detach(dev); 294ef2ee5d0SMichal Meloun return (rv); 295ef2ee5d0SMichal Meloun } 296ef2ee5d0SMichal Meloun 297ef2ee5d0SMichal Meloun static device_method_t ehci_methods[] = { 298ef2ee5d0SMichal Meloun /* Device interface */ 299ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, tegra_ehci_probe), 300ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, tegra_ehci_attach), 301ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, tegra_ehci_detach), 302ef2ee5d0SMichal Meloun DEVMETHOD(device_suspend, bus_generic_suspend), 303ef2ee5d0SMichal Meloun DEVMETHOD(device_resume, bus_generic_resume), 304ef2ee5d0SMichal Meloun DEVMETHOD(device_shutdown, bus_generic_shutdown), 305ef2ee5d0SMichal Meloun 306ef2ee5d0SMichal Meloun /* Bus interface */ 307ef2ee5d0SMichal Meloun DEVMETHOD(bus_print_child, bus_generic_print_child), 308ef2ee5d0SMichal Meloun 309ef2ee5d0SMichal Meloun DEVMETHOD_END 310ef2ee5d0SMichal Meloun }; 311ef2ee5d0SMichal Meloun 3124bda238aSMichal Meloun static DEFINE_CLASS_0(ehci, ehci_driver, ehci_methods, 3134bda238aSMichal Meloun sizeof(struct tegra_ehci_softc)); 314289f133bSJohn Baldwin DRIVER_MODULE(tegra_ehci, simplebus, ehci_driver, NULL, NULL); 3154bda238aSMichal Meloun MODULE_DEPEND(tegra_ehci, usb, 1, 1, 1); 316