111e11bd6SRuslan Bukin /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 311e11bd6SRuslan Bukin * 411e11bd6SRuslan Bukin * Copyright (c) 2018 Ruslan Bukin <br@bsdpad.com> 511e11bd6SRuslan Bukin * All rights reserved. 611e11bd6SRuslan Bukin * 711e11bd6SRuslan Bukin * This software was developed by BAE Systems, the University of Cambridge 811e11bd6SRuslan Bukin * Computer Laboratory, and Memorial University under DARPA/AFRL contract 911e11bd6SRuslan Bukin * FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent Computing 1011e11bd6SRuslan Bukin * (TC) research program. 1111e11bd6SRuslan Bukin * 1211e11bd6SRuslan Bukin * Redistribution and use in source and binary forms, with or without 1311e11bd6SRuslan Bukin * modification, are permitted provided that the following conditions 1411e11bd6SRuslan Bukin * are met: 1511e11bd6SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 1611e11bd6SRuslan Bukin * notice, this list of conditions and the following disclaimer. 1711e11bd6SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 1811e11bd6SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 1911e11bd6SRuslan Bukin * documentation and/or other materials provided with the distribution. 2011e11bd6SRuslan Bukin * 2111e11bd6SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2211e11bd6SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2311e11bd6SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2411e11bd6SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2511e11bd6SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2611e11bd6SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2711e11bd6SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2811e11bd6SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2911e11bd6SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3011e11bd6SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3111e11bd6SRuslan Bukin * SUCH DAMAGE. 3211e11bd6SRuslan Bukin */ 3311e11bd6SRuslan Bukin 3411e11bd6SRuslan Bukin #include <sys/cdefs.h> 3511e11bd6SRuslan Bukin #include "opt_bus.h" 3611e11bd6SRuslan Bukin 3711e11bd6SRuslan Bukin #include <sys/param.h> 3811e11bd6SRuslan Bukin #include <sys/systm.h> 3911e11bd6SRuslan Bukin #include <sys/bus.h> 4011e11bd6SRuslan Bukin #include <sys/rman.h> 4111e11bd6SRuslan Bukin #include <sys/condvar.h> 4211e11bd6SRuslan Bukin #include <sys/kernel.h> 4311e11bd6SRuslan Bukin #include <sys/module.h> 4411e11bd6SRuslan Bukin 4511e11bd6SRuslan Bukin #include <dev/ofw/ofw_bus.h> 4611e11bd6SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h> 4711e11bd6SRuslan Bukin 4811e11bd6SRuslan Bukin #include <dev/usb/usb.h> 4911e11bd6SRuslan Bukin #include <dev/usb/usbdi.h> 5011e11bd6SRuslan Bukin 5111e11bd6SRuslan Bukin #include <dev/usb/usb_core.h> 5211e11bd6SRuslan Bukin #include <dev/usb/usb_busdma.h> 5311e11bd6SRuslan Bukin #include <dev/usb/usb_process.h> 5411e11bd6SRuslan Bukin #include <dev/usb/usb_util.h> 5511e11bd6SRuslan Bukin 5611e11bd6SRuslan Bukin #include <dev/usb/usb_controller.h> 5711e11bd6SRuslan Bukin #include <dev/usb/usb_bus.h> 5811e11bd6SRuslan Bukin #include <dev/usb/controller/ehci.h> 5911e11bd6SRuslan Bukin #include <dev/usb/controller/ehcireg.h> 6011e11bd6SRuslan Bukin 6111e11bd6SRuslan Bukin struct ehci_msm_softc { 6211e11bd6SRuslan Bukin ehci_softc_t base; 6311e11bd6SRuslan Bukin struct resource *res[3]; 6411e11bd6SRuslan Bukin }; 6511e11bd6SRuslan Bukin 6611e11bd6SRuslan Bukin static struct resource_spec ehci_msm_spec[] = { 6711e11bd6SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 6811e11bd6SRuslan Bukin { SYS_RES_MEMORY, 1, RF_ACTIVE }, 6911e11bd6SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 7011e11bd6SRuslan Bukin { -1, 0 } 7111e11bd6SRuslan Bukin }; 7211e11bd6SRuslan Bukin 7311e11bd6SRuslan Bukin #define EHCI_HC_DEVSTR "Qualcomm USB 2.0 controller" 7411e11bd6SRuslan Bukin 7511e11bd6SRuslan Bukin static device_attach_t ehci_msm_attach; 7611e11bd6SRuslan Bukin static device_detach_t ehci_msm_detach; 7711e11bd6SRuslan Bukin 7811e11bd6SRuslan Bukin static int 7911e11bd6SRuslan Bukin ehci_msm_probe(device_t dev) 8011e11bd6SRuslan Bukin { 8111e11bd6SRuslan Bukin 8211e11bd6SRuslan Bukin if (!ofw_bus_is_compatible(dev, "qcom,ci-hdrc")) 8311e11bd6SRuslan Bukin return (ENXIO); 8411e11bd6SRuslan Bukin 8511e11bd6SRuslan Bukin device_set_desc(dev, EHCI_HC_DEVSTR); 8611e11bd6SRuslan Bukin 8711e11bd6SRuslan Bukin return (BUS_PROBE_DEFAULT); 8811e11bd6SRuslan Bukin } 8911e11bd6SRuslan Bukin 9011e11bd6SRuslan Bukin static int 9111e11bd6SRuslan Bukin ehci_msm_attach(device_t dev) 9211e11bd6SRuslan Bukin { 9311e11bd6SRuslan Bukin struct ehci_msm_softc *esc; 9411e11bd6SRuslan Bukin bus_space_handle_t bsh; 9511e11bd6SRuslan Bukin ehci_softc_t *sc; 9611e11bd6SRuslan Bukin int err; 9711e11bd6SRuslan Bukin 9811e11bd6SRuslan Bukin esc = device_get_softc(dev); 9911e11bd6SRuslan Bukin sc = &esc->base; 10011e11bd6SRuslan Bukin sc->sc_bus.parent = dev; 10111e11bd6SRuslan Bukin sc->sc_bus.devices = sc->sc_devices; 10211e11bd6SRuslan Bukin sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 10311e11bd6SRuslan Bukin sc->sc_bus.dma_bits = 32; 10411e11bd6SRuslan Bukin 10511e11bd6SRuslan Bukin if (bus_alloc_resources(dev, ehci_msm_spec, esc->res)) { 10611e11bd6SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 10711e11bd6SRuslan Bukin return (ENXIO); 10811e11bd6SRuslan Bukin } 10911e11bd6SRuslan Bukin 11011e11bd6SRuslan Bukin sc->sc_io_tag = rman_get_bustag(esc->res[0]); 11111e11bd6SRuslan Bukin 11211e11bd6SRuslan Bukin /* Get all DMA memory */ 11311e11bd6SRuslan Bukin if (usb_bus_mem_alloc_all(&sc->sc_bus, 11411e11bd6SRuslan Bukin USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) { 11511e11bd6SRuslan Bukin return (ENOMEM); 11611e11bd6SRuslan Bukin } 11711e11bd6SRuslan Bukin 11811e11bd6SRuslan Bukin /* EHCI registers */ 11911e11bd6SRuslan Bukin sc->sc_io_tag = rman_get_bustag(esc->res[0]); 12011e11bd6SRuslan Bukin bsh = rman_get_bushandle(esc->res[0]); 12111e11bd6SRuslan Bukin sc->sc_io_size = rman_get_size(esc->res[0]); 12211e11bd6SRuslan Bukin 12311e11bd6SRuslan Bukin if (bus_space_subregion(sc->sc_io_tag, bsh, 0x100, 12411e11bd6SRuslan Bukin sc->sc_io_size, &sc->sc_io_hdl) != 0) 12511e11bd6SRuslan Bukin panic("%s: unable to subregion USB host registers", 12611e11bd6SRuslan Bukin device_get_name(dev)); 12711e11bd6SRuslan Bukin 1285b56413dSWarner Losh sc->sc_bus.bdev = device_add_child(dev, "usbus", DEVICE_UNIT_ANY); 12911e11bd6SRuslan Bukin if (!sc->sc_bus.bdev) { 13011e11bd6SRuslan Bukin device_printf(dev, "Could not add USB device\n"); 13111e11bd6SRuslan Bukin goto error; 13211e11bd6SRuslan Bukin } 13311e11bd6SRuslan Bukin device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 13411e11bd6SRuslan Bukin device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 13511e11bd6SRuslan Bukin 13611e11bd6SRuslan Bukin sprintf(sc->sc_vendor, "Qualcomm"); 13711e11bd6SRuslan Bukin 13811e11bd6SRuslan Bukin err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, 13911e11bd6SRuslan Bukin NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 14011e11bd6SRuslan Bukin if (err) { 14111e11bd6SRuslan Bukin device_printf(dev, "Could not setup irq, %d\n", err); 14211e11bd6SRuslan Bukin sc->sc_intr_hdl = NULL; 14311e11bd6SRuslan Bukin goto error; 14411e11bd6SRuslan Bukin } 14511e11bd6SRuslan Bukin 14611e11bd6SRuslan Bukin sc->sc_flags |= EHCI_SCFLG_DONTRESET | EHCI_SCFLG_NORESTERM; 14711e11bd6SRuslan Bukin 14811e11bd6SRuslan Bukin err = ehci_init(sc); 14911e11bd6SRuslan Bukin if (!err) { 15011e11bd6SRuslan Bukin sc->sc_flags |= EHCI_SCFLG_DONEINIT; 15111e11bd6SRuslan Bukin err = device_probe_and_attach(sc->sc_bus.bdev); 15211e11bd6SRuslan Bukin } 15311e11bd6SRuslan Bukin 15411e11bd6SRuslan Bukin if (err) { 15511e11bd6SRuslan Bukin device_printf(dev, "USB init failed err=%d\n", err); 15611e11bd6SRuslan Bukin goto error; 15711e11bd6SRuslan Bukin } 15811e11bd6SRuslan Bukin return (0); 15911e11bd6SRuslan Bukin 16011e11bd6SRuslan Bukin error: 16111e11bd6SRuslan Bukin ehci_msm_detach(dev); 16211e11bd6SRuslan Bukin return (ENXIO); 16311e11bd6SRuslan Bukin } 16411e11bd6SRuslan Bukin 16511e11bd6SRuslan Bukin static int 16611e11bd6SRuslan Bukin ehci_msm_detach(device_t dev) 16711e11bd6SRuslan Bukin { 16811e11bd6SRuslan Bukin ehci_softc_t *sc; 16911e11bd6SRuslan Bukin device_t bdev; 17011e11bd6SRuslan Bukin int err; 17111e11bd6SRuslan Bukin 17211e11bd6SRuslan Bukin sc = device_get_softc(dev); 17311e11bd6SRuslan Bukin 174*3ddaf820SJohn Baldwin err = bus_generic_detach(dev); 175*3ddaf820SJohn Baldwin if (err != 0) 176*3ddaf820SJohn Baldwin return (err); 17711e11bd6SRuslan Bukin 17811e11bd6SRuslan Bukin if (sc->sc_irq_res && sc->sc_intr_hdl) { 17911e11bd6SRuslan Bukin /* only call ehci_detach() after ehci_init() */ 18011e11bd6SRuslan Bukin ehci_detach(sc); 18111e11bd6SRuslan Bukin 18211e11bd6SRuslan Bukin err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); 18311e11bd6SRuslan Bukin if (err) 18411e11bd6SRuslan Bukin device_printf(dev, "Could not tear down irq, %d\n", 18511e11bd6SRuslan Bukin err); 18611e11bd6SRuslan Bukin sc->sc_intr_hdl = NULL; 18711e11bd6SRuslan Bukin } 18811e11bd6SRuslan Bukin 18911e11bd6SRuslan Bukin if (sc->sc_irq_res) { 19011e11bd6SRuslan Bukin bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 19111e11bd6SRuslan Bukin sc->sc_irq_res = NULL; 19211e11bd6SRuslan Bukin } 19311e11bd6SRuslan Bukin if (sc->sc_io_res) { 19411e11bd6SRuslan Bukin bus_release_resource(dev, SYS_RES_MEMORY, 0, 19511e11bd6SRuslan Bukin sc->sc_io_res); 19611e11bd6SRuslan Bukin sc->sc_io_res = NULL; 19711e11bd6SRuslan Bukin } 19811e11bd6SRuslan Bukin 19911e11bd6SRuslan Bukin usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 20011e11bd6SRuslan Bukin 20111e11bd6SRuslan Bukin return (0); 20211e11bd6SRuslan Bukin } 20311e11bd6SRuslan Bukin 20411e11bd6SRuslan Bukin static device_method_t ehci_methods[] = { 20511e11bd6SRuslan Bukin /* Device interface */ 20611e11bd6SRuslan Bukin DEVMETHOD(device_probe, ehci_msm_probe), 20711e11bd6SRuslan Bukin DEVMETHOD(device_attach, ehci_msm_attach), 20811e11bd6SRuslan Bukin DEVMETHOD(device_detach, ehci_msm_detach), 20911e11bd6SRuslan Bukin DEVMETHOD(device_suspend, bus_generic_suspend), 21011e11bd6SRuslan Bukin DEVMETHOD(device_resume, bus_generic_resume), 21111e11bd6SRuslan Bukin DEVMETHOD(device_shutdown, bus_generic_shutdown), 21211e11bd6SRuslan Bukin DEVMETHOD_END 21311e11bd6SRuslan Bukin }; 21411e11bd6SRuslan Bukin 21511e11bd6SRuslan Bukin static driver_t ehci_driver = { 21611e11bd6SRuslan Bukin .name = "ehci", 21711e11bd6SRuslan Bukin .methods = ehci_methods, 21811e11bd6SRuslan Bukin .size = sizeof(ehci_softc_t), 21911e11bd6SRuslan Bukin }; 22011e11bd6SRuslan Bukin 221bc9372d7SJohn Baldwin DRIVER_MODULE(ehci_msm, simplebus, ehci_driver, 0, 0); 22211e11bd6SRuslan Bukin MODULE_DEPEND(ehci_msm, usb, 1, 1, 1); 223