ar71xx_ohci.c revision 199233
185587Sobrien/*- 285587Sobrien * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 385587Sobrien * All rights reserved. 485587Sobrien * 585587Sobrien * Redistribution and use in source and binary forms, with or without 685587Sobrien * modification, are permitted provided that the following conditions 785587Sobrien * are met: 885587Sobrien * 1. Redistributions of source code must retain the above copyright 985587Sobrien * notice unmodified, this list of conditions, and the following 1085587Sobrien * disclaimer. 1185587Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1285587Sobrien * notice, this list of conditions and the following disclaimer in the 1385587Sobrien * documentation and/or other materials provided with the distribution. 1485587Sobrien * 1585587Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1685587Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1785587Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1885587Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1985587Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2085587Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2185587Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2285587Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2385587Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2485587Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25170331Srafan * SUCH DAMAGE. 2685587Sobrien */ 27201989Sru 28201989Sru#include <sys/cdefs.h> 29201989Sru__FBSDID("$FreeBSD$"); 3085587Sobrien 3185587Sobrien#include <sys/param.h> 3285587Sobrien#include <sys/systm.h> 3385587Sobrien#include <sys/bus.h> 3485587Sobrien#include <sys/rman.h> 3585587Sobrien#include <sys/condvar.h> 3685587Sobrien#include <sys/kernel.h> 3785587Sobrien#include <sys/module.h> 3885587Sobrien 39118194Sru#include <dev/usb/usb.h> 4085587Sobrien#include <dev/usb/usbdi.h> 4185587Sobrien 4285587Sobrien#include <dev/usb/usb_core.h> 4385587Sobrien#include <dev/usb/usb_busdma.h> 4485587Sobrien#include <dev/usb/usb_process.h> 4585587Sobrien#include <dev/usb/usb_util.h> 4685587Sobrien 4785587Sobrien#include <dev/usb/usb_controller.h> 4885587Sobrien#include <dev/usb/usb_bus.h> 4985587Sobrien#include <dev/usb/controller/ohci.h> 50170331Srafan#include <dev/usb/controller/ohcireg.h> 5185587Sobrien 5285587Sobrien#include <sys/rman.h> 5385587Sobrien 54170331Srafanstatic int ar71xx_ohci_attach(device_t dev); 5585587Sobrienstatic int ar71xx_ohci_detach(device_t dev); 5685587Sobrienstatic int ar71xx_ohci_probe(device_t dev); 5785587Sobrien 5885587Sobrienstruct ar71xx_ohci_softc 5985587Sobrien{ 6085587Sobrien struct ohci_softc sc_ohci; 6185587Sobrien}; 6285587Sobrien 6385587Sobrienstatic int 6485587Sobrienar71xx_ohci_probe(device_t dev) 6585587Sobrien{ 6685587Sobrien device_set_desc(dev, "AR71XX integrated OHCI controller"); 6785587Sobrien return (BUS_PROBE_DEFAULT); 6885587Sobrien} 6985587Sobrien 7085587Sobrienstatic int 7185587Sobrienar71xx_ohci_attach(device_t dev) 7285587Sobrien{ 7385587Sobrien struct ar71xx_ohci_softc *sc = device_get_softc(dev); 7485587Sobrien int err; 7585587Sobrien int rid; 7685587Sobrien 7785587Sobrien /* initialise some bus fields */ 7885587Sobrien sc->sc_ohci.sc_bus.parent = dev; 7985587Sobrien sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; 8085587Sobrien sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; 8185587Sobrien 82107806Sobrien /* get all DMA memory */ 8385587Sobrien if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, 8485587Sobrien USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { 8585587Sobrien return (ENOMEM); 8685587Sobrien } 8785587Sobrien 8885587Sobrien sc->sc_ohci.sc_dev = dev; 8985587Sobrien 9085587Sobrien rid = 0; 9185587Sobrien sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 9285587Sobrien RF_ACTIVE); 9385587Sobrien if (sc->sc_ohci.sc_io_res == NULL) { 9485587Sobrien err = ENOMEM; 9585587Sobrien goto error; 9685587Sobrien } 9785587Sobrien sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); 9885587Sobrien sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); 9985587Sobrien sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); 10090902Sdes 10185587Sobrien rid = 0; 10285587Sobrien sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 10385587Sobrien RF_ACTIVE); 10485587Sobrien if (sc->sc_ohci.sc_irq_res == NULL) { 10585587Sobrien err = ENOMEM; 10685587Sobrien goto error; 10785587Sobrien } 10885587Sobrien sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); 10985587Sobrien if (sc->sc_ohci.sc_bus.bdev == NULL) { 11085587Sobrien err = ENOMEM; 11185587Sobrien goto error; 11285587Sobrien } 11385587Sobrien device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); 11485587Sobrien 11585587Sobrien err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 11685587Sobrien INTR_TYPE_BIO | INTR_MPSAFE, NULL, 11785587Sobrien (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); 11885587Sobrien if (err) { 11985587Sobrien err = ENXIO; 12085587Sobrien goto error; 12185587Sobrien } 12285587Sobrien 12385587Sobrien strlcpy(sc->sc_ohci.sc_vendor, "Atheros", sizeof(sc->sc_ohci.sc_vendor)); 124107806Sobrien 12585587Sobrien bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); 12685587Sobrien 12785587Sobrien err = ohci_init(&sc->sc_ohci); 12885587Sobrien if (!err) 12985587Sobrien err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); 13085587Sobrien 13185587Sobrien if (err) 13285587Sobrien goto error; 13385587Sobrien return (0); 13485587Sobrien 13585587Sobrienerror: 13685587Sobrien if (err) { 13785587Sobrien ar71xx_ohci_detach(dev); 13885587Sobrien return (err); 13985587Sobrien } 14085587Sobrien return (err); 14185587Sobrien} 14285587Sobrien 14385587Sobrienstatic int 14485587Sobrienar71xx_ohci_detach(device_t dev) 14585587Sobrien{ 14685587Sobrien struct ar71xx_ohci_softc *sc = device_get_softc(dev); 14785587Sobrien device_t bdev; 14885587Sobrien 14985587Sobrien if (sc->sc_ohci.sc_bus.bdev) { 15085587Sobrien bdev = sc->sc_ohci.sc_bus.bdev; 15185587Sobrien device_detach(bdev); 15285587Sobrien device_delete_child(dev, bdev); 15385587Sobrien } 15485587Sobrien /* during module unload there are lots of children leftover */ 15585587Sobrien device_delete_all_children(dev); 15685587Sobrien 15785587Sobrien /* 15885587Sobrien * Put the controller into reset, then disable clocks and do 15985587Sobrien * the MI tear down. We have to disable the clocks/hardware 16085587Sobrien * after we do the rest of the teardown. We also disable the 16185587Sobrien * clocks in the opposite order we acquire them, but that 16285587Sobrien * doesn't seem to be absolutely necessary. We free up the 16385587Sobrien * clocks after we disable them, so the system could, in 16485587Sobrien * theory, reuse them. 16585587Sobrien */ 16685587Sobrien bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 16785587Sobrien OHCI_CONTROL, 0); 16885587Sobrien 16985587Sobrien if (sc->sc_ohci.sc_intr_hdl) { 17085587Sobrien bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); 17185587Sobrien sc->sc_ohci.sc_intr_hdl = NULL; 17285587Sobrien } 17385587Sobrien 17485587Sobrien if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { 17585587Sobrien /* 17685587Sobrien * only call ohci_detach() after ohci_init() 17785587Sobrien */ 17885587Sobrien ohci_detach(&sc->sc_ohci); 17985587Sobrien 18085587Sobrien bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); 18185587Sobrien sc->sc_ohci.sc_irq_res = NULL; 18285587Sobrien } 18385587Sobrien if (sc->sc_ohci.sc_io_res) { 18485587Sobrien bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res); 18585587Sobrien sc->sc_ohci.sc_io_res = NULL; 18685587Sobrien sc->sc_ohci.sc_io_tag = 0; 18785587Sobrien sc->sc_ohci.sc_io_hdl = 0; 18885587Sobrien } 189170331Srafan usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); 19085587Sobrien 19185587Sobrien return (0); 19285587Sobrien} 19385587Sobrien 19485587Sobrienstatic device_method_t ohci_methods[] = { 19585587Sobrien /* Device interface */ 19685587Sobrien DEVMETHOD(device_probe, ar71xx_ohci_probe), 19785587Sobrien DEVMETHOD(device_attach, ar71xx_ohci_attach), 19885587Sobrien DEVMETHOD(device_detach, ar71xx_ohci_detach), 19985587Sobrien DEVMETHOD(device_shutdown, bus_generic_shutdown), 20085587Sobrien 20185587Sobrien /* Bus interface */ 20285587Sobrien DEVMETHOD(bus_print_child, bus_generic_print_child), 20385587Sobrien 20485587Sobrien {0, 0} 20585587Sobrien}; 20685587Sobrien 20785587Sobrienstatic driver_t ohci_driver = { 20885587Sobrien "ohci", 20985587Sobrien ohci_methods, 21085587Sobrien sizeof(struct ar71xx_ohci_softc), 21185587Sobrien}; 21285587Sobrien 21385587Sobrienstatic devclass_t ohci_devclass; 214170331Srafan 21585587SobrienDRIVER_MODULE(ohci, apb, ohci_driver, ohci_devclass, 0, 0); 21685587Sobrien