1201468Srpaulo/*- 2201468Srpaulo * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com> 3201468Srpaulo * All rights reserved. 4201468Srpaulo * 5201468Srpaulo * Redistribution and use in source and binary forms, with or without 6201468Srpaulo * modification, are permitted provided that the following conditions 7201468Srpaulo * are met: 8201468Srpaulo * 1. Redistributions of source code must retain the above copyright 9201468Srpaulo * notice, this list of conditions and the following disclaimer. 10201468Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11201468Srpaulo * notice, this list of conditions and the following disclaimer in the 12201468Srpaulo * documentation and/or other materials provided with the distribution. 13201468Srpaulo * 14201468Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15201468Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16201468Srpaulo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17201468Srpaulo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18201468Srpaulo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19201468Srpaulo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20201468Srpaulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21201468Srpaulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22201468Srpaulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23201468Srpaulo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24201468Srpaulo */ 25201468Srpaulo 26201468Srpaulo#include <sys/cdefs.h> 27201468Srpaulo__FBSDID("$FreeBSD$"); 28201468Srpaulo 29201468Srpaulo#include <sys/stdint.h> 30201468Srpaulo#include <sys/stddef.h> 31201468Srpaulo#include <sys/param.h> 32201468Srpaulo#include <sys/queue.h> 33201468Srpaulo#include <sys/types.h> 34201468Srpaulo#include <sys/systm.h> 35201468Srpaulo#include <sys/kernel.h> 36201468Srpaulo#include <sys/bus.h> 37201468Srpaulo#include <sys/module.h> 38201468Srpaulo#include <sys/lock.h> 39201468Srpaulo#include <sys/mutex.h> 40201468Srpaulo#include <sys/condvar.h> 41201468Srpaulo#include <sys/sysctl.h> 42201468Srpaulo#include <sys/sx.h> 43201468Srpaulo#include <sys/unistd.h> 44201468Srpaulo#include <sys/callout.h> 45201468Srpaulo#include <sys/malloc.h> 46201468Srpaulo#include <sys/priv.h> 47201468Srpaulo 48201468Srpaulo#include <dev/usb/usb.h> 49201468Srpaulo#include <dev/usb/usbdi.h> 50201468Srpaulo 51201468Srpaulo#include <dev/usb/usb_core.h> 52201468Srpaulo#include <dev/usb/usb_busdma.h> 53201468Srpaulo#include <dev/usb/usb_process.h> 54201468Srpaulo#include <dev/usb/usb_util.h> 55201468Srpaulo 56201468Srpaulo#include <dev/usb/usb_controller.h> 57201468Srpaulo#include <dev/usb/usb_bus.h> 58201468Srpaulo#include <dev/usb/controller/ohci.h> 59201468Srpaulo#include <dev/usb/controller/ohcireg.h> 60201468Srpaulo 61201468Srpaulo#include <sys/rman.h> 62201468Srpaulo 63201468Srpaulo#include <arm/econa/econa_reg.h> 64201468Srpaulo 65201468Srpaulo#define MEM_RID 0 66201468Srpaulo 67201468Srpaulostatic device_probe_t ohci_ec_probe; 68201468Srpaulostatic device_attach_t ohci_ec_attach; 69201468Srpaulostatic device_detach_t ohci_ec_detach; 70201468Srpaulo 71201468Srpaulostruct ec_ohci_softc { 72201468Srpaulo struct ohci_softc sc_ohci; /* must be first */ 73201468Srpaulo}; 74201468Srpaulo 75201468Srpaulostatic int 76201468Srpauloohci_ec_probe(device_t dev) 77201468Srpaulo{ 78201468Srpaulo device_set_desc(dev, "Econa integrated OHCI controller"); 79201468Srpaulo return (BUS_PROBE_DEFAULT); 80201468Srpaulo} 81201468Srpaulo 82201468Srpaulostatic int 83201468Srpauloohci_ec_attach(device_t dev) 84201468Srpaulo{ 85201468Srpaulo struct ec_ohci_softc *sc = device_get_softc(dev); 86201468Srpaulo bus_space_handle_t bsh; 87201468Srpaulo int err; 88201468Srpaulo int rid; 89201468Srpaulo 90201468Srpaulo /* initialise some bus fields */ 91201468Srpaulo sc->sc_ohci.sc_bus.parent = dev; 92201468Srpaulo sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; 93201468Srpaulo sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; 94201468Srpaulo 95201468Srpaulo /* get all DMA memory */ 96201468Srpaulo if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, 97201468Srpaulo USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { 98201468Srpaulo return (ENOMEM); 99201468Srpaulo } 100201468Srpaulo sc->sc_ohci.sc_dev = dev; 101201468Srpaulo 102201468Srpaulo rid = MEM_RID; 103201468Srpaulo 104201468Srpaulo sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 105201468Srpaulo &rid, RF_ACTIVE); 106201468Srpaulo 107201468Srpaulo if (!(sc->sc_ohci.sc_io_res)) { 108201468Srpaulo err = ENOMEM; 109201468Srpaulo goto error; 110201468Srpaulo } 111201468Srpaulo sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); 112201468Srpaulo bsh = rman_get_bushandle(sc->sc_ohci.sc_io_res); 113201468Srpaulo /* Undocumented magic initialization */ 114201468Srpaulo bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x04, 0x146); 115201468Srpaulo 116201468Srpaulo bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x44, 0x0200); 117201468Srpaulo 118201468Srpaulo DELAY(1000); 119201468Srpaulo 120201468Srpaulo sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); 121201468Srpaulo 122201468Srpaulo if (bus_space_subregion(sc->sc_ohci.sc_io_tag, bsh, 0x4000000, 123201468Srpaulo sc->sc_ohci.sc_io_size, &sc->sc_ohci.sc_io_hdl) != 0) 124201468Srpaulo panic("%s: unable to subregion USB host registers", 125201468Srpaulo device_get_name(dev)); 126201468Srpaulo 127201468Srpaulo rid = 0; 128201468Srpaulo sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 129201468Srpaulo RF_ACTIVE); 130201468Srpaulo if (!(sc->sc_ohci.sc_irq_res)) { 131201468Srpaulo goto error; 132201468Srpaulo } 133201468Srpaulo sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); 134201468Srpaulo if (!(sc->sc_ohci.sc_bus.bdev)) { 135201468Srpaulo goto error; 136201468Srpaulo } 137201468Srpaulo device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); 138201468Srpaulo 139201468Srpaulo strlcpy(sc->sc_ohci.sc_vendor, "Cavium", 140201468Srpaulo sizeof(sc->sc_ohci.sc_vendor)); 141201468Srpaulo 142201468Srpaulo#if (__FreeBSD_version >= 700031) 143201468Srpaulo err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 144201468Srpaulo INTR_TYPE_BIO | INTR_MPSAFE, NULL, 145201468Srpaulo (driver_intr_t *)ohci_interrupt, sc, 146201468Srpaulo &sc->sc_ohci.sc_intr_hdl); 147201468Srpaulo#else 148201468Srpaulo err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 149201468Srpaulo INTR_TYPE_BIO | INTR_MPSAFE, 150201468Srpaulo (driver_intr_t *)ohci_interrupt, sc, 151201468Srpaulo &sc->sc_ohci.sc_intr_hdl); 152201468Srpaulo#endif 153201468Srpaulo if (err) { 154201468Srpaulo sc->sc_ohci.sc_intr_hdl = NULL; 155201468Srpaulo goto error; 156201468Srpaulo } 157201468Srpaulo 158201468Srpaulo bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 159201468Srpaulo OHCI_CONTROL, 0); 160201468Srpaulo 161201468Srpaulo err = ohci_init(&sc->sc_ohci); 162201468Srpaulo if (!err) { 163201468Srpaulo err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); 164201468Srpaulo } 165201468Srpaulo if (err) { 166201468Srpaulo goto error; 167201468Srpaulo } 168201468Srpaulo return (0); 169201468Srpaulo 170201468Srpauloerror: 171201468Srpaulo ohci_ec_detach(dev); 172201468Srpaulo return (ENXIO); 173201468Srpaulo} 174201468Srpaulo 175201468Srpaulostatic int 176201468Srpauloohci_ec_detach(device_t dev) 177201468Srpaulo{ 178201468Srpaulo struct ec_ohci_softc *sc = device_get_softc(dev); 179201468Srpaulo device_t bdev; 180201468Srpaulo int err; 181201468Srpaulo 182201468Srpaulo if (sc->sc_ohci.sc_bus.bdev) { 183201468Srpaulo bdev = sc->sc_ohci.sc_bus.bdev; 184201468Srpaulo device_detach(bdev); 185201468Srpaulo device_delete_child(dev, bdev); 186201468Srpaulo } 187201468Srpaulo /* during module unload there are lots of children leftover */ 188227849Shselasky device_delete_children(dev); 189201468Srpaulo 190201468Srpaulo bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 191201468Srpaulo OHCI_CONTROL, 0); 192201468Srpaulo 193201468Srpaulo if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { 194201468Srpaulo /* 195201468Srpaulo * only call ohci_detach() after ohci_init() 196201468Srpaulo */ 197201468Srpaulo ohci_detach(&sc->sc_ohci); 198201468Srpaulo 199201468Srpaulo err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, 200201468Srpaulo sc->sc_ohci.sc_intr_hdl); 201201468Srpaulo sc->sc_ohci.sc_intr_hdl = NULL; 202201468Srpaulo } 203201468Srpaulo if (sc->sc_ohci.sc_irq_res) { 204201468Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 0, 205201468Srpaulo sc->sc_ohci.sc_irq_res); 206201468Srpaulo sc->sc_ohci.sc_irq_res = NULL; 207201468Srpaulo } 208201468Srpaulo if (sc->sc_ohci.sc_io_res) { 209201468Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, 210201468Srpaulo sc->sc_ohci.sc_io_res); 211201468Srpaulo sc->sc_ohci.sc_io_res = NULL; 212201468Srpaulo } 213201468Srpaulo usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); 214201468Srpaulo 215201468Srpaulo return (0); 216201468Srpaulo} 217201468Srpaulo 218201468Srpaulostatic device_method_t ohci_methods[] = { 219201468Srpaulo /* Device interface */ 220201468Srpaulo DEVMETHOD(device_probe, ohci_ec_probe), 221201468Srpaulo DEVMETHOD(device_attach, ohci_ec_attach), 222201468Srpaulo DEVMETHOD(device_detach, ohci_ec_detach), 223228483Shselasky DEVMETHOD(device_resume, bus_generic_resume), 224228483Shselasky DEVMETHOD(device_suspend, bus_generic_suspend), 225201468Srpaulo DEVMETHOD(device_shutdown, bus_generic_shutdown), 226201468Srpaulo 227227843Smarius DEVMETHOD_END 228201468Srpaulo}; 229201468Srpaulo 230201468Srpaulostatic driver_t ohci_driver = { 231228483Shselasky .name = "ohci", 232228483Shselasky .methods = ohci_methods, 233228483Shselasky .size = sizeof(struct ec_ohci_softc), 234201468Srpaulo}; 235201468Srpaulo 236201468Srpaulostatic devclass_t ohci_devclass; 237201468Srpaulo 238201468SrpauloDRIVER_MODULE(ohci, econaarm, ohci_driver, ohci_devclass, 0, 0); 239201468SrpauloMODULE_DEPEND(ohci, usb, 1, 1, 1); 240