1188884Sgonzo/*- 2188884Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3188884Sgonzo * All rights reserved. 4188884Sgonzo * 5188884Sgonzo * Redistribution and use in source and binary forms, with or without 6188884Sgonzo * modification, are permitted provided that the following conditions 7188884Sgonzo * are met: 8188884Sgonzo * 1. Redistributions of source code must retain the above copyright 9188884Sgonzo * notice unmodified, this list of conditions, and the following 10188884Sgonzo * disclaimer. 11188884Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12188884Sgonzo * notice, this list of conditions and the following disclaimer in the 13188884Sgonzo * documentation and/or other materials provided with the distribution. 14188884Sgonzo * 15188884Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16188884Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17188884Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18188884Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19188884Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20188884Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21188884Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22188884Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23188884Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24188884Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25188884Sgonzo * SUCH DAMAGE. 26188884Sgonzo */ 27188884Sgonzo 28188884Sgonzo#include <sys/cdefs.h> 29188884Sgonzo__FBSDID("$FreeBSD$"); 30188884Sgonzo 31195985Sgonzo#include <sys/param.h> 32195985Sgonzo#include <sys/systm.h> 33195985Sgonzo#include <sys/bus.h> 34195985Sgonzo#include <sys/rman.h> 35195985Sgonzo#include <sys/condvar.h> 36195985Sgonzo#include <sys/kernel.h> 37195985Sgonzo#include <sys/module.h> 38195985Sgonzo 39188884Sgonzo#include <dev/usb/usb.h> 40195985Sgonzo#include <dev/usb/usbdi.h> 41188884Sgonzo 42191086Sgonzo#include <dev/usb/usb_core.h> 43191086Sgonzo#include <dev/usb/usb_busdma.h> 44191086Sgonzo#include <dev/usb/usb_process.h> 45191086Sgonzo#include <dev/usb/usb_util.h> 46188884Sgonzo 47191086Sgonzo#include <dev/usb/usb_controller.h> 48191086Sgonzo#include <dev/usb/usb_bus.h> 49191086Sgonzo#include <dev/usb/controller/ohci.h> 50199233Sgonzo#include <dev/usb/controller/ohcireg.h> 51191086Sgonzo 52188884Sgonzostatic int ar71xx_ohci_attach(device_t dev); 53188884Sgonzostatic int ar71xx_ohci_detach(device_t dev); 54188884Sgonzostatic int ar71xx_ohci_probe(device_t dev); 55188884Sgonzo 56188884Sgonzostruct ar71xx_ohci_softc 57188884Sgonzo{ 58188884Sgonzo struct ohci_softc sc_ohci; 59188884Sgonzo}; 60188884Sgonzo 61188884Sgonzostatic int 62188884Sgonzoar71xx_ohci_probe(device_t dev) 63188884Sgonzo{ 64188884Sgonzo device_set_desc(dev, "AR71XX integrated OHCI controller"); 65188884Sgonzo return (BUS_PROBE_DEFAULT); 66188884Sgonzo} 67188884Sgonzo 68188884Sgonzostatic int 69188884Sgonzoar71xx_ohci_attach(device_t dev) 70188884Sgonzo{ 71188884Sgonzo struct ar71xx_ohci_softc *sc = device_get_softc(dev); 72188884Sgonzo int err; 73188884Sgonzo int rid; 74188884Sgonzo 75191101Sgonzo /* initialise some bus fields */ 76191101Sgonzo sc->sc_ohci.sc_bus.parent = dev; 77191101Sgonzo sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; 78191101Sgonzo sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; 79191101Sgonzo 80191101Sgonzo /* get all DMA memory */ 81195985Sgonzo if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, 82191101Sgonzo USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { 83191101Sgonzo return (ENOMEM); 84191101Sgonzo } 85191101Sgonzo 86191101Sgonzo sc->sc_ohci.sc_dev = dev; 87191101Sgonzo 88188884Sgonzo rid = 0; 89191086Sgonzo sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 90188884Sgonzo RF_ACTIVE); 91191086Sgonzo if (sc->sc_ohci.sc_io_res == NULL) { 92188884Sgonzo err = ENOMEM; 93188884Sgonzo goto error; 94188884Sgonzo } 95191086Sgonzo sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); 96191086Sgonzo sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); 97191086Sgonzo sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); 98188884Sgonzo 99188884Sgonzo rid = 0; 100191086Sgonzo sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 101188884Sgonzo RF_ACTIVE); 102191086Sgonzo if (sc->sc_ohci.sc_irq_res == NULL) { 103188884Sgonzo err = ENOMEM; 104188884Sgonzo goto error; 105188884Sgonzo } 106191101Sgonzo sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); 107188884Sgonzo if (sc->sc_ohci.sc_bus.bdev == NULL) { 108188884Sgonzo err = ENOMEM; 109188884Sgonzo goto error; 110188884Sgonzo } 111188884Sgonzo device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); 112188884Sgonzo 113191086Sgonzo err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 114191086Sgonzo INTR_TYPE_BIO | INTR_MPSAFE, NULL, 115191086Sgonzo (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); 116188884Sgonzo if (err) { 117188884Sgonzo err = ENXIO; 118188884Sgonzo goto error; 119188884Sgonzo } 120188884Sgonzo 121191086Sgonzo strlcpy(sc->sc_ohci.sc_vendor, "Atheros", sizeof(sc->sc_ohci.sc_vendor)); 122188884Sgonzo 123191086Sgonzo bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); 124188884Sgonzo 125188884Sgonzo err = ohci_init(&sc->sc_ohci); 126191101Sgonzo if (!err) 127188884Sgonzo err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); 128188884Sgonzo 129191101Sgonzo if (err) 130191101Sgonzo goto error; 131191101Sgonzo return (0); 132191101Sgonzo 133188884Sgonzoerror: 134188884Sgonzo if (err) { 135188884Sgonzo ar71xx_ohci_detach(dev); 136188884Sgonzo return (err); 137188884Sgonzo } 138188884Sgonzo return (err); 139188884Sgonzo} 140188884Sgonzo 141188884Sgonzostatic int 142188884Sgonzoar71xx_ohci_detach(device_t dev) 143188884Sgonzo{ 144188884Sgonzo struct ar71xx_ohci_softc *sc = device_get_softc(dev); 145191101Sgonzo device_t bdev; 146188884Sgonzo 147191101Sgonzo if (sc->sc_ohci.sc_bus.bdev) { 148191101Sgonzo bdev = sc->sc_ohci.sc_bus.bdev; 149191101Sgonzo device_detach(bdev); 150191101Sgonzo device_delete_child(dev, bdev); 151191101Sgonzo } 152191101Sgonzo /* during module unload there are lots of children leftover */ 153227849Shselasky device_delete_children(dev); 154191101Sgonzo 155191101Sgonzo /* 156191101Sgonzo * Put the controller into reset, then disable clocks and do 157191101Sgonzo * the MI tear down. We have to disable the clocks/hardware 158191101Sgonzo * after we do the rest of the teardown. We also disable the 159191101Sgonzo * clocks in the opposite order we acquire them, but that 160191101Sgonzo * doesn't seem to be absolutely necessary. We free up the 161191101Sgonzo * clocks after we disable them, so the system could, in 162191101Sgonzo * theory, reuse them. 163191101Sgonzo */ 164191101Sgonzo bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 165191101Sgonzo OHCI_CONTROL, 0); 166191101Sgonzo 167191086Sgonzo if (sc->sc_ohci.sc_intr_hdl) { 168191086Sgonzo bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); 169191086Sgonzo sc->sc_ohci.sc_intr_hdl = NULL; 170188884Sgonzo } 171191101Sgonzo 172191101Sgonzo if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { 173191101Sgonzo /* 174191101Sgonzo * only call ohci_detach() after ohci_init() 175191101Sgonzo */ 176191101Sgonzo ohci_detach(&sc->sc_ohci); 177191101Sgonzo 178191086Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); 179191086Sgonzo sc->sc_ohci.sc_irq_res = NULL; 180188884Sgonzo } 181191086Sgonzo if (sc->sc_ohci.sc_io_res) { 182191086Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res); 183191086Sgonzo sc->sc_ohci.sc_io_res = NULL; 184191086Sgonzo sc->sc_ohci.sc_io_tag = 0; 185191086Sgonzo sc->sc_ohci.sc_io_hdl = 0; 186188884Sgonzo } 187195985Sgonzo usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); 188191101Sgonzo 189188884Sgonzo return (0); 190188884Sgonzo} 191188884Sgonzo 192188884Sgonzostatic device_method_t ohci_methods[] = { 193188884Sgonzo /* Device interface */ 194188884Sgonzo DEVMETHOD(device_probe, ar71xx_ohci_probe), 195188884Sgonzo DEVMETHOD(device_attach, ar71xx_ohci_attach), 196188884Sgonzo DEVMETHOD(device_detach, ar71xx_ohci_detach), 197228483Shselasky DEVMETHOD(device_suspend, bus_generic_suspend), 198228483Shselasky DEVMETHOD(device_resume, bus_generic_resume), 199188884Sgonzo DEVMETHOD(device_shutdown, bus_generic_shutdown), 200188884Sgonzo 201227843Smarius DEVMETHOD_END 202188884Sgonzo}; 203188884Sgonzo 204188884Sgonzostatic driver_t ohci_driver = { 205228483Shselasky .name = "ohci", 206228483Shselasky .methods = ohci_methods, 207228483Shselasky .size = sizeof(struct ar71xx_ohci_softc), 208188884Sgonzo}; 209188884Sgonzo 210188884Sgonzostatic devclass_t ohci_devclass; 211188884Sgonzo 212188884SgonzoDRIVER_MODULE(ohci, apb, ohci_driver, ohci_devclass, 0, 0); 213