1d71896a7SEmmanuel Vadot /*- 2d71896a7SEmmanuel Vadot * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3d71896a7SEmmanuel Vadot * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org> 4d71896a7SEmmanuel Vadot * All rights reserved. 5d71896a7SEmmanuel Vadot * 6d71896a7SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 7d71896a7SEmmanuel Vadot * modification, are permitted provided that the following conditions 8d71896a7SEmmanuel Vadot * are met: 9d71896a7SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 10d71896a7SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 11d71896a7SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 12d71896a7SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 13d71896a7SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 14d71896a7SEmmanuel Vadot * 15d71896a7SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16d71896a7SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17d71896a7SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18d71896a7SEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19d71896a7SEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20d71896a7SEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21d71896a7SEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22d71896a7SEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23d71896a7SEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24d71896a7SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25d71896a7SEmmanuel Vadot * SUCH DAMAGE. 26d71896a7SEmmanuel Vadot */ 27d71896a7SEmmanuel Vadot 28d71896a7SEmmanuel Vadot /* 29d71896a7SEmmanuel Vadot * Generic OHCI driver based on AT91 OHCI 30d71896a7SEmmanuel Vadot */ 31d71896a7SEmmanuel Vadot 32d71896a7SEmmanuel Vadot #include <sys/cdefs.h> 33d71896a7SEmmanuel Vadot __FBSDID("$FreeBSD$"); 34d71896a7SEmmanuel Vadot 35d71896a7SEmmanuel Vadot #include <sys/param.h> 36d71896a7SEmmanuel Vadot #include <sys/systm.h> 37d71896a7SEmmanuel Vadot #include <sys/bus.h> 38d71896a7SEmmanuel Vadot #include <sys/rman.h> 39d71896a7SEmmanuel Vadot #include <sys/condvar.h> 40d71896a7SEmmanuel Vadot #include <sys/kernel.h> 41d71896a7SEmmanuel Vadot #include <sys/module.h> 42d71896a7SEmmanuel Vadot 43d71896a7SEmmanuel Vadot #include <machine/bus.h> 44d71896a7SEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 45d71896a7SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h> 46d71896a7SEmmanuel Vadot 47d71896a7SEmmanuel Vadot #include <dev/usb/usb.h> 48d71896a7SEmmanuel Vadot #include <dev/usb/usbdi.h> 49d71896a7SEmmanuel Vadot 50d71896a7SEmmanuel Vadot #include <dev/usb/usb_core.h> 51d71896a7SEmmanuel Vadot #include <dev/usb/usb_busdma.h> 52d71896a7SEmmanuel Vadot #include <dev/usb/usb_process.h> 53d71896a7SEmmanuel Vadot #include <dev/usb/usb_util.h> 54d71896a7SEmmanuel Vadot 55d71896a7SEmmanuel Vadot #include <dev/usb/usb_controller.h> 56d71896a7SEmmanuel Vadot #include <dev/usb/usb_bus.h> 57d71896a7SEmmanuel Vadot #include <dev/usb/controller/ohci.h> 58d71896a7SEmmanuel Vadot #include <dev/usb/controller/ohcireg.h> 59d71896a7SEmmanuel Vadot 60d71896a7SEmmanuel Vadot #ifdef EXT_RESOURCES 61d71896a7SEmmanuel Vadot #include <dev/extres/clk/clk.h> 62d71896a7SEmmanuel Vadot #include <dev/extres/hwreset/hwreset.h> 63d71896a7SEmmanuel Vadot #endif 64d71896a7SEmmanuel Vadot 65d71896a7SEmmanuel Vadot #include "generic_usb_if.h" 66d71896a7SEmmanuel Vadot 67d71896a7SEmmanuel Vadot #ifdef EXT_RESOURCES 68d71896a7SEmmanuel Vadot struct clk_list { 69d71896a7SEmmanuel Vadot TAILQ_ENTRY(clk_list) next; 70d71896a7SEmmanuel Vadot clk_t clk; 71d71896a7SEmmanuel Vadot }; 72d71896a7SEmmanuel Vadot #endif 73d71896a7SEmmanuel Vadot 74d71896a7SEmmanuel Vadot struct generic_ohci_softc { 75d71896a7SEmmanuel Vadot ohci_softc_t ohci_sc; 76d71896a7SEmmanuel Vadot 77d71896a7SEmmanuel Vadot #ifdef EXT_RESOURCES 78d71896a7SEmmanuel Vadot hwreset_t rst; 79d71896a7SEmmanuel Vadot TAILQ_HEAD(, clk_list) clk_list; 80d71896a7SEmmanuel Vadot #endif 81d71896a7SEmmanuel Vadot }; 82d71896a7SEmmanuel Vadot 83d71896a7SEmmanuel Vadot static int generic_ohci_detach(device_t); 84d71896a7SEmmanuel Vadot 85d71896a7SEmmanuel Vadot static int 86d71896a7SEmmanuel Vadot generic_ohci_probe(device_t dev) 87d71896a7SEmmanuel Vadot { 88d71896a7SEmmanuel Vadot 89d71896a7SEmmanuel Vadot if (!ofw_bus_status_okay(dev)) 90d71896a7SEmmanuel Vadot return (ENXIO); 91d71896a7SEmmanuel Vadot 92d71896a7SEmmanuel Vadot if (!ofw_bus_is_compatible(dev, "generic-ohci")) 93d71896a7SEmmanuel Vadot return (ENXIO); 94d71896a7SEmmanuel Vadot 95d71896a7SEmmanuel Vadot device_set_desc(dev, "Generic OHCI Controller"); 96d71896a7SEmmanuel Vadot 97d71896a7SEmmanuel Vadot return (BUS_PROBE_DEFAULT); 98d71896a7SEmmanuel Vadot } 99d71896a7SEmmanuel Vadot 100d71896a7SEmmanuel Vadot static int 101d71896a7SEmmanuel Vadot generic_ohci_attach(device_t dev) 102d71896a7SEmmanuel Vadot { 103d71896a7SEmmanuel Vadot struct generic_ohci_softc *sc = device_get_softc(dev); 104d71896a7SEmmanuel Vadot int err, rid; 105d71896a7SEmmanuel Vadot #ifdef EXT_RESOURCES 106d71896a7SEmmanuel Vadot int off; 107d71896a7SEmmanuel Vadot struct clk_list *clkp; 108d71896a7SEmmanuel Vadot clk_t clk; 109d71896a7SEmmanuel Vadot #endif 110d71896a7SEmmanuel Vadot 111d71896a7SEmmanuel Vadot sc->ohci_sc.sc_bus.parent = dev; 112d71896a7SEmmanuel Vadot sc->ohci_sc.sc_bus.devices = sc->ohci_sc.sc_devices; 113d71896a7SEmmanuel Vadot sc->ohci_sc.sc_bus.devices_max = OHCI_MAX_DEVICES; 114d71896a7SEmmanuel Vadot sc->ohci_sc.sc_bus.dma_bits = 32; 115d71896a7SEmmanuel Vadot 116d71896a7SEmmanuel Vadot /* get all DMA memory */ 117d71896a7SEmmanuel Vadot if (usb_bus_mem_alloc_all(&sc->ohci_sc.sc_bus, 118d71896a7SEmmanuel Vadot USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { 119d71896a7SEmmanuel Vadot return (ENOMEM); 120d71896a7SEmmanuel Vadot } 121d71896a7SEmmanuel Vadot 122d71896a7SEmmanuel Vadot rid = 0; 123d71896a7SEmmanuel Vadot sc->ohci_sc.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 124d71896a7SEmmanuel Vadot &rid, RF_ACTIVE); 125d71896a7SEmmanuel Vadot if (sc->ohci_sc.sc_io_res == 0) { 126d71896a7SEmmanuel Vadot err = ENOMEM; 127d71896a7SEmmanuel Vadot goto error; 128d71896a7SEmmanuel Vadot } 129d71896a7SEmmanuel Vadot 130d71896a7SEmmanuel Vadot sc->ohci_sc.sc_io_tag = rman_get_bustag(sc->ohci_sc.sc_io_res); 131d71896a7SEmmanuel Vadot sc->ohci_sc.sc_io_hdl = rman_get_bushandle(sc->ohci_sc.sc_io_res); 132d71896a7SEmmanuel Vadot sc->ohci_sc.sc_io_size = rman_get_size(sc->ohci_sc.sc_io_res); 133d71896a7SEmmanuel Vadot 134d71896a7SEmmanuel Vadot rid = 0; 135d71896a7SEmmanuel Vadot sc->ohci_sc.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 136d71896a7SEmmanuel Vadot RF_ACTIVE); 137d71896a7SEmmanuel Vadot if (sc->ohci_sc.sc_irq_res == 0) { 138d71896a7SEmmanuel Vadot err = ENXIO; 139d71896a7SEmmanuel Vadot goto error; 140d71896a7SEmmanuel Vadot } 141d71896a7SEmmanuel Vadot sc->ohci_sc.sc_bus.bdev = device_add_child(dev, "usbus", -1); 142d71896a7SEmmanuel Vadot if (sc->ohci_sc.sc_bus.bdev == 0) { 143d71896a7SEmmanuel Vadot err = ENXIO; 144d71896a7SEmmanuel Vadot goto error; 145d71896a7SEmmanuel Vadot } 146d71896a7SEmmanuel Vadot device_set_ivars(sc->ohci_sc.sc_bus.bdev, &sc->ohci_sc.sc_bus); 147d71896a7SEmmanuel Vadot 148d71896a7SEmmanuel Vadot strlcpy(sc->ohci_sc.sc_vendor, "Generic", 149d71896a7SEmmanuel Vadot sizeof(sc->ohci_sc.sc_vendor)); 150d71896a7SEmmanuel Vadot 151d71896a7SEmmanuel Vadot err = bus_setup_intr(dev, sc->ohci_sc.sc_irq_res, 152d71896a7SEmmanuel Vadot INTR_TYPE_BIO | INTR_MPSAFE, NULL, 153d71896a7SEmmanuel Vadot (driver_intr_t *)ohci_interrupt, sc, &sc->ohci_sc.sc_intr_hdl); 154d71896a7SEmmanuel Vadot if (err) { 155d71896a7SEmmanuel Vadot sc->ohci_sc.sc_intr_hdl = NULL; 156d71896a7SEmmanuel Vadot goto error; 157d71896a7SEmmanuel Vadot } 158d71896a7SEmmanuel Vadot 159d71896a7SEmmanuel Vadot #ifdef EXT_RESOURCES 160d71896a7SEmmanuel Vadot TAILQ_INIT(&sc->clk_list); 161d71896a7SEmmanuel Vadot /* Enable clock */ 162d71896a7SEmmanuel Vadot for (off = 0; clk_get_by_ofw_index(dev, off, &clk) == 0; off++) { 163d71896a7SEmmanuel Vadot err = clk_enable(clk); 164d71896a7SEmmanuel Vadot if (err != 0) { 165d71896a7SEmmanuel Vadot device_printf(dev, "Could not enable clock %s\n", 166d71896a7SEmmanuel Vadot clk_get_name(clk)); 167d71896a7SEmmanuel Vadot goto error; 168d71896a7SEmmanuel Vadot } 169*6e2392aeSHans Petter Selasky clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO); 170d71896a7SEmmanuel Vadot clkp->clk = clk; 171d71896a7SEmmanuel Vadot TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); 172d71896a7SEmmanuel Vadot } 173d71896a7SEmmanuel Vadot 174d71896a7SEmmanuel Vadot /* De-assert reset */ 175d71896a7SEmmanuel Vadot if (hwreset_get_by_ofw_idx(dev, 0, &sc->rst) == 0) { 176d71896a7SEmmanuel Vadot err = hwreset_deassert(sc->rst); 177d71896a7SEmmanuel Vadot if (err != 0) { 178d71896a7SEmmanuel Vadot device_printf(dev, "Could not de-assert reset %d\n", 179d71896a7SEmmanuel Vadot off); 180d71896a7SEmmanuel Vadot goto error; 181d71896a7SEmmanuel Vadot } 182d71896a7SEmmanuel Vadot } 183d71896a7SEmmanuel Vadot #endif 184d71896a7SEmmanuel Vadot 185d71896a7SEmmanuel Vadot if (GENERIC_USB_INIT(dev) != 0) { 186d71896a7SEmmanuel Vadot err = ENXIO; 187d71896a7SEmmanuel Vadot goto error; 188d71896a7SEmmanuel Vadot } 189d71896a7SEmmanuel Vadot 190d71896a7SEmmanuel Vadot err = ohci_init(&sc->ohci_sc); 191d71896a7SEmmanuel Vadot if (err == 0) 192d71896a7SEmmanuel Vadot err = device_probe_and_attach(sc->ohci_sc.sc_bus.bdev); 193d71896a7SEmmanuel Vadot if (err) 194d71896a7SEmmanuel Vadot goto error; 195d71896a7SEmmanuel Vadot 196d71896a7SEmmanuel Vadot return (0); 197d71896a7SEmmanuel Vadot error: 198d71896a7SEmmanuel Vadot generic_ohci_detach(dev); 199d71896a7SEmmanuel Vadot return (err); 200d71896a7SEmmanuel Vadot } 201d71896a7SEmmanuel Vadot 202d71896a7SEmmanuel Vadot static int 203d71896a7SEmmanuel Vadot generic_ohci_detach(device_t dev) 204d71896a7SEmmanuel Vadot { 205d71896a7SEmmanuel Vadot struct generic_ohci_softc *sc = device_get_softc(dev); 206d71896a7SEmmanuel Vadot device_t bdev; 207d71896a7SEmmanuel Vadot int err; 208d71896a7SEmmanuel Vadot #ifdef EXT_RESOURCES 209d71896a7SEmmanuel Vadot struct clk_list *clk, *clk_tmp; 210d71896a7SEmmanuel Vadot #endif 211d71896a7SEmmanuel Vadot 212d71896a7SEmmanuel Vadot if (sc->ohci_sc.sc_bus.bdev) { 213d71896a7SEmmanuel Vadot bdev = sc->ohci_sc.sc_bus.bdev; 214d71896a7SEmmanuel Vadot device_detach(bdev); 215d71896a7SEmmanuel Vadot device_delete_child(dev, bdev); 216d71896a7SEmmanuel Vadot } 217d71896a7SEmmanuel Vadot 218d71896a7SEmmanuel Vadot /* during module unload there are lots of children leftover */ 219d71896a7SEmmanuel Vadot device_delete_children(dev); 220d71896a7SEmmanuel Vadot 221d71896a7SEmmanuel Vadot /* 222d71896a7SEmmanuel Vadot * Put the controller into reset, then disable clocks and do 223d71896a7SEmmanuel Vadot * the MI tear down. We have to disable the clocks/hardware 224d71896a7SEmmanuel Vadot * after we do the rest of the teardown. We also disable the 225d71896a7SEmmanuel Vadot * clocks in the opposite order we acquire them, but that 226d71896a7SEmmanuel Vadot * doesn't seem to be absolutely necessary. We free up the 227d71896a7SEmmanuel Vadot * clocks after we disable them, so the system could, in 228d71896a7SEmmanuel Vadot * theory, reuse them. 229d71896a7SEmmanuel Vadot */ 230d71896a7SEmmanuel Vadot bus_space_write_4(sc->ohci_sc.sc_io_tag, sc->ohci_sc.sc_io_hdl, 231d71896a7SEmmanuel Vadot OHCI_CONTROL, 0); 232d71896a7SEmmanuel Vadot 233d71896a7SEmmanuel Vadot if (sc->ohci_sc.sc_irq_res && sc->ohci_sc.sc_intr_hdl) { 234d71896a7SEmmanuel Vadot /* 235d71896a7SEmmanuel Vadot * only call ohci_detach() after ohci_init() 236d71896a7SEmmanuel Vadot */ 237d71896a7SEmmanuel Vadot ohci_detach(&sc->ohci_sc); 238d71896a7SEmmanuel Vadot 239d71896a7SEmmanuel Vadot err = bus_teardown_intr(dev, sc->ohci_sc.sc_irq_res, 240d71896a7SEmmanuel Vadot sc->ohci_sc.sc_intr_hdl); 241d71896a7SEmmanuel Vadot sc->ohci_sc.sc_intr_hdl = NULL; 242d71896a7SEmmanuel Vadot } 243d71896a7SEmmanuel Vadot if (sc->ohci_sc.sc_irq_res) { 244d71896a7SEmmanuel Vadot bus_release_resource(dev, SYS_RES_IRQ, 0, 245d71896a7SEmmanuel Vadot sc->ohci_sc.sc_irq_res); 246d71896a7SEmmanuel Vadot sc->ohci_sc.sc_irq_res = NULL; 247d71896a7SEmmanuel Vadot } 248d71896a7SEmmanuel Vadot if (sc->ohci_sc.sc_io_res) { 249d71896a7SEmmanuel Vadot bus_release_resource(dev, SYS_RES_MEMORY, 0, 250d71896a7SEmmanuel Vadot sc->ohci_sc.sc_io_res); 251d71896a7SEmmanuel Vadot sc->ohci_sc.sc_io_res = NULL; 252d71896a7SEmmanuel Vadot } 253d71896a7SEmmanuel Vadot usb_bus_mem_free_all(&sc->ohci_sc.sc_bus, &ohci_iterate_hw_softc); 254d71896a7SEmmanuel Vadot 255d71896a7SEmmanuel Vadot #ifdef EXT_RESOURCES 256d71896a7SEmmanuel Vadot /* Disable clock */ 257d71896a7SEmmanuel Vadot TAILQ_FOREACH_SAFE(clk, &sc->clk_list, next, clk_tmp) { 258d71896a7SEmmanuel Vadot err = clk_disable(clk->clk); 259d71896a7SEmmanuel Vadot if (err != 0) 260d71896a7SEmmanuel Vadot device_printf(dev, "Could not disable clock %s\n", 261d71896a7SEmmanuel Vadot clk_get_name(clk->clk)); 262d71896a7SEmmanuel Vadot err = clk_release(clk->clk); 263d71896a7SEmmanuel Vadot if (err != 0) 264d71896a7SEmmanuel Vadot device_printf(dev, "Could not release clock %s\n", 265d71896a7SEmmanuel Vadot clk_get_name(clk->clk)); 266d71896a7SEmmanuel Vadot TAILQ_REMOVE(&sc->clk_list, clk, next); 267d71896a7SEmmanuel Vadot free(clk, M_DEVBUF); 268d71896a7SEmmanuel Vadot } 269d71896a7SEmmanuel Vadot 270d71896a7SEmmanuel Vadot /* De-assert reset */ 271d71896a7SEmmanuel Vadot if (sc->rst) { 272d71896a7SEmmanuel Vadot err = hwreset_assert(sc->rst); 273d71896a7SEmmanuel Vadot if (err != 0) 274d71896a7SEmmanuel Vadot device_printf(dev, "Could not assert reset\n"); 275d71896a7SEmmanuel Vadot hwreset_release(sc->rst); 276d71896a7SEmmanuel Vadot } 277d71896a7SEmmanuel Vadot #endif 278d71896a7SEmmanuel Vadot 279d71896a7SEmmanuel Vadot if (GENERIC_USB_DEINIT(dev) != 0) 280d71896a7SEmmanuel Vadot return (ENXIO); 281d71896a7SEmmanuel Vadot 282d71896a7SEmmanuel Vadot return (0); 283d71896a7SEmmanuel Vadot } 284d71896a7SEmmanuel Vadot 285d71896a7SEmmanuel Vadot static device_method_t generic_ohci_methods[] = { 286d71896a7SEmmanuel Vadot /* Device interface */ 287d71896a7SEmmanuel Vadot DEVMETHOD(device_probe, generic_ohci_probe), 288d71896a7SEmmanuel Vadot DEVMETHOD(device_attach, generic_ohci_attach), 289d71896a7SEmmanuel Vadot DEVMETHOD(device_detach, generic_ohci_detach), 290d71896a7SEmmanuel Vadot 291d71896a7SEmmanuel Vadot DEVMETHOD(device_suspend, bus_generic_suspend), 292d71896a7SEmmanuel Vadot DEVMETHOD(device_resume, bus_generic_resume), 293d71896a7SEmmanuel Vadot DEVMETHOD(device_shutdown, bus_generic_shutdown), 294d71896a7SEmmanuel Vadot 295d71896a7SEmmanuel Vadot DEVMETHOD_END 296d71896a7SEmmanuel Vadot }; 297d71896a7SEmmanuel Vadot 298d71896a7SEmmanuel Vadot driver_t generic_ohci_driver = { 299d71896a7SEmmanuel Vadot .name = "ohci", 300d71896a7SEmmanuel Vadot .methods = generic_ohci_methods, 301d71896a7SEmmanuel Vadot .size = sizeof(struct generic_ohci_softc), 302d71896a7SEmmanuel Vadot }; 303d71896a7SEmmanuel Vadot 304d71896a7SEmmanuel Vadot static devclass_t generic_ohci_devclass; 305d71896a7SEmmanuel Vadot 306d71896a7SEmmanuel Vadot DRIVER_MODULE(ohci, simplebus, generic_ohci_driver, 307d71896a7SEmmanuel Vadot generic_ohci_devclass, 0, 0); 308d71896a7SEmmanuel Vadot MODULE_DEPEND(ohci, usb, 1, 1, 1); 309