ohci_ec.c revision 201468
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: head/sys/arm/econa/ohci_ec.c 201468 2010-01-04 03:35:45Z rpaulo $"); 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/linker_set.h> 38201468Srpaulo#include <sys/module.h> 39201468Srpaulo#include <sys/lock.h> 40201468Srpaulo#include <sys/mutex.h> 41201468Srpaulo#include <sys/condvar.h> 42201468Srpaulo#include <sys/sysctl.h> 43201468Srpaulo#include <sys/sx.h> 44201468Srpaulo#include <sys/unistd.h> 45201468Srpaulo#include <sys/callout.h> 46201468Srpaulo#include <sys/malloc.h> 47201468Srpaulo#include <sys/priv.h> 48201468Srpaulo 49201468Srpaulo#include <dev/usb/usb.h> 50201468Srpaulo#include <dev/usb/usbdi.h> 51201468Srpaulo 52201468Srpaulo#include <dev/usb/usb_core.h> 53201468Srpaulo#include <dev/usb/usb_busdma.h> 54201468Srpaulo#include <dev/usb/usb_process.h> 55201468Srpaulo#include <dev/usb/usb_util.h> 56201468Srpaulo 57201468Srpaulo#include <dev/usb/usb_controller.h> 58201468Srpaulo#include <dev/usb/usb_bus.h> 59201468Srpaulo#include <dev/usb/controller/ohci.h> 60201468Srpaulo#include <dev/usb/controller/ohcireg.h> 61201468Srpaulo 62201468Srpaulo#include <sys/rman.h> 63201468Srpaulo 64201468Srpaulo#include <arm/econa/econa_reg.h> 65201468Srpaulo 66201468Srpaulo#define MEM_RID 0 67201468Srpaulo 68201468Srpaulostatic device_probe_t ohci_ec_probe; 69201468Srpaulostatic device_attach_t ohci_ec_attach; 70201468Srpaulostatic device_detach_t ohci_ec_detach; 71201468Srpaulo 72201468Srpaulostruct ec_ohci_softc { 73201468Srpaulo struct ohci_softc sc_ohci; /* must be first */ 74201468Srpaulo}; 75201468Srpaulo 76201468Srpaulostatic int 77201468Srpauloohci_ec_probe(device_t dev) 78201468Srpaulo{ 79201468Srpaulo device_set_desc(dev, "Econa integrated OHCI controller"); 80201468Srpaulo return (BUS_PROBE_DEFAULT); 81201468Srpaulo} 82201468Srpaulo 83201468Srpaulostatic int 84201468Srpauloohci_ec_attach(device_t dev) 85201468Srpaulo{ 86201468Srpaulo struct ec_ohci_softc *sc = device_get_softc(dev); 87201468Srpaulo bus_space_handle_t bsh; 88201468Srpaulo int err; 89201468Srpaulo int rid; 90201468Srpaulo 91201468Srpaulo /* initialise some bus fields */ 92201468Srpaulo sc->sc_ohci.sc_bus.parent = dev; 93201468Srpaulo sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; 94201468Srpaulo sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; 95201468Srpaulo 96201468Srpaulo /* get all DMA memory */ 97201468Srpaulo if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, 98201468Srpaulo USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { 99201468Srpaulo return (ENOMEM); 100201468Srpaulo } 101201468Srpaulo sc->sc_ohci.sc_dev = dev; 102201468Srpaulo 103201468Srpaulo rid = MEM_RID; 104201468Srpaulo 105201468Srpaulo sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 106201468Srpaulo &rid, RF_ACTIVE); 107201468Srpaulo 108201468Srpaulo if (!(sc->sc_ohci.sc_io_res)) { 109201468Srpaulo err = ENOMEM; 110201468Srpaulo goto error; 111201468Srpaulo } 112201468Srpaulo sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); 113201468Srpaulo bsh = rman_get_bushandle(sc->sc_ohci.sc_io_res); 114201468Srpaulo /* Undocumented magic initialization */ 115201468Srpaulo bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x04, 0x146); 116201468Srpaulo 117201468Srpaulo bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x44, 0x0200); 118201468Srpaulo 119201468Srpaulo DELAY(1000); 120201468Srpaulo 121201468Srpaulo sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); 122201468Srpaulo 123201468Srpaulo if (bus_space_subregion(sc->sc_ohci.sc_io_tag, bsh, 0x4000000, 124201468Srpaulo sc->sc_ohci.sc_io_size, &sc->sc_ohci.sc_io_hdl) != 0) 125201468Srpaulo panic("%s: unable to subregion USB host registers", 126201468Srpaulo device_get_name(dev)); 127201468Srpaulo 128201468Srpaulo rid = 0; 129201468Srpaulo sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 130201468Srpaulo RF_ACTIVE); 131201468Srpaulo if (!(sc->sc_ohci.sc_irq_res)) { 132201468Srpaulo goto error; 133201468Srpaulo } 134201468Srpaulo sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); 135201468Srpaulo if (!(sc->sc_ohci.sc_bus.bdev)) { 136201468Srpaulo goto error; 137201468Srpaulo } 138201468Srpaulo device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); 139201468Srpaulo 140201468Srpaulo strlcpy(sc->sc_ohci.sc_vendor, "Cavium", 141201468Srpaulo sizeof(sc->sc_ohci.sc_vendor)); 142201468Srpaulo 143201468Srpaulo#if (__FreeBSD_version >= 700031) 144201468Srpaulo err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 145201468Srpaulo INTR_TYPE_BIO | INTR_MPSAFE, NULL, 146201468Srpaulo (driver_intr_t *)ohci_interrupt, sc, 147201468Srpaulo &sc->sc_ohci.sc_intr_hdl); 148201468Srpaulo#else 149201468Srpaulo err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 150201468Srpaulo INTR_TYPE_BIO | INTR_MPSAFE, 151201468Srpaulo (driver_intr_t *)ohci_interrupt, sc, 152201468Srpaulo &sc->sc_ohci.sc_intr_hdl); 153201468Srpaulo#endif 154201468Srpaulo if (err) { 155201468Srpaulo sc->sc_ohci.sc_intr_hdl = NULL; 156201468Srpaulo goto error; 157201468Srpaulo } 158201468Srpaulo 159201468Srpaulo bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 160201468Srpaulo OHCI_CONTROL, 0); 161201468Srpaulo 162201468Srpaulo err = ohci_init(&sc->sc_ohci); 163201468Srpaulo if (!err) { 164201468Srpaulo err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); 165201468Srpaulo } 166201468Srpaulo if (err) { 167201468Srpaulo goto error; 168201468Srpaulo } 169201468Srpaulo return (0); 170201468Srpaulo 171201468Srpauloerror: 172201468Srpaulo ohci_ec_detach(dev); 173201468Srpaulo return (ENXIO); 174201468Srpaulo} 175201468Srpaulo 176201468Srpaulostatic int 177201468Srpauloohci_ec_detach(device_t dev) 178201468Srpaulo{ 179201468Srpaulo struct ec_ohci_softc *sc = device_get_softc(dev); 180201468Srpaulo device_t bdev; 181201468Srpaulo int err; 182201468Srpaulo 183201468Srpaulo if (sc->sc_ohci.sc_bus.bdev) { 184201468Srpaulo bdev = sc->sc_ohci.sc_bus.bdev; 185201468Srpaulo device_detach(bdev); 186201468Srpaulo device_delete_child(dev, bdev); 187201468Srpaulo } 188201468Srpaulo /* during module unload there are lots of children leftover */ 189201468Srpaulo device_delete_all_children(dev); 190201468Srpaulo 191201468Srpaulo bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 192201468Srpaulo OHCI_CONTROL, 0); 193201468Srpaulo 194201468Srpaulo if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { 195201468Srpaulo /* 196201468Srpaulo * only call ohci_detach() after ohci_init() 197201468Srpaulo */ 198201468Srpaulo ohci_detach(&sc->sc_ohci); 199201468Srpaulo 200201468Srpaulo err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, 201201468Srpaulo sc->sc_ohci.sc_intr_hdl); 202201468Srpaulo sc->sc_ohci.sc_intr_hdl = NULL; 203201468Srpaulo } 204201468Srpaulo if (sc->sc_ohci.sc_irq_res) { 205201468Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 0, 206201468Srpaulo sc->sc_ohci.sc_irq_res); 207201468Srpaulo sc->sc_ohci.sc_irq_res = NULL; 208201468Srpaulo } 209201468Srpaulo if (sc->sc_ohci.sc_io_res) { 210201468Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, 211201468Srpaulo sc->sc_ohci.sc_io_res); 212201468Srpaulo sc->sc_ohci.sc_io_res = NULL; 213201468Srpaulo } 214201468Srpaulo usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); 215201468Srpaulo 216201468Srpaulo return (0); 217201468Srpaulo} 218201468Srpaulo 219201468Srpaulostatic device_method_t ohci_methods[] = { 220201468Srpaulo /* Device interface */ 221201468Srpaulo DEVMETHOD(device_probe, ohci_ec_probe), 222201468Srpaulo DEVMETHOD(device_attach, ohci_ec_attach), 223201468Srpaulo DEVMETHOD(device_detach, ohci_ec_detach), 224201468Srpaulo DEVMETHOD(device_shutdown, bus_generic_shutdown), 225201468Srpaulo 226201468Srpaulo /* Bus interface */ 227201468Srpaulo DEVMETHOD(bus_print_child, bus_generic_print_child), 228201468Srpaulo 229201468Srpaulo {0, 0} 230201468Srpaulo}; 231201468Srpaulo 232201468Srpaulostatic driver_t ohci_driver = { 233201468Srpaulo "ohci", 234201468Srpaulo ohci_methods, 235201468Srpaulo sizeof(struct ec_ohci_softc), 236201468Srpaulo}; 237201468Srpaulo 238201468Srpaulostatic devclass_t ohci_devclass; 239201468Srpaulo 240201468SrpauloDRIVER_MODULE(ohci, econaarm, ohci_driver, ohci_devclass, 0, 0); 241201468SrpauloMODULE_DEPEND(ohci, usb, 1, 1, 1); 242