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: stable/10/sys/arm/cavium/cns11xx/ohci_ec.c 308402 2016-11-07 09:19:04Z hselasky $"); 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 63264219Srpaulo#include <arm/cavium/cns11xx/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; 94278278Shselasky sc->sc_ohci.sc_bus.dma_bits = 32; 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 int err; 181201468Srpaulo 182201468Srpaulo /* during module unload there are lots of children leftover */ 183227849Shselasky device_delete_children(dev); 184201468Srpaulo 185201468Srpaulo bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 186201468Srpaulo OHCI_CONTROL, 0); 187201468Srpaulo 188201468Srpaulo if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { 189201468Srpaulo /* 190201468Srpaulo * only call ohci_detach() after ohci_init() 191201468Srpaulo */ 192201468Srpaulo ohci_detach(&sc->sc_ohci); 193201468Srpaulo 194201468Srpaulo err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, 195201468Srpaulo sc->sc_ohci.sc_intr_hdl); 196201468Srpaulo sc->sc_ohci.sc_intr_hdl = NULL; 197201468Srpaulo } 198201468Srpaulo if (sc->sc_ohci.sc_irq_res) { 199201468Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 0, 200201468Srpaulo sc->sc_ohci.sc_irq_res); 201201468Srpaulo sc->sc_ohci.sc_irq_res = NULL; 202201468Srpaulo } 203201468Srpaulo if (sc->sc_ohci.sc_io_res) { 204201468Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, 205201468Srpaulo sc->sc_ohci.sc_io_res); 206201468Srpaulo sc->sc_ohci.sc_io_res = NULL; 207201468Srpaulo } 208201468Srpaulo usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); 209201468Srpaulo 210201468Srpaulo return (0); 211201468Srpaulo} 212201468Srpaulo 213201468Srpaulostatic device_method_t ohci_methods[] = { 214201468Srpaulo /* Device interface */ 215201468Srpaulo DEVMETHOD(device_probe, ohci_ec_probe), 216201468Srpaulo DEVMETHOD(device_attach, ohci_ec_attach), 217201468Srpaulo DEVMETHOD(device_detach, ohci_ec_detach), 218228483Shselasky DEVMETHOD(device_resume, bus_generic_resume), 219228483Shselasky DEVMETHOD(device_suspend, bus_generic_suspend), 220201468Srpaulo DEVMETHOD(device_shutdown, bus_generic_shutdown), 221201468Srpaulo 222227843Smarius DEVMETHOD_END 223201468Srpaulo}; 224201468Srpaulo 225201468Srpaulostatic driver_t ohci_driver = { 226228483Shselasky .name = "ohci", 227228483Shselasky .methods = ohci_methods, 228228483Shselasky .size = sizeof(struct ec_ohci_softc), 229201468Srpaulo}; 230201468Srpaulo 231201468Srpaulostatic devclass_t ohci_devclass; 232201468Srpaulo 233201468SrpauloDRIVER_MODULE(ohci, econaarm, ohci_driver, ohci_devclass, 0, 0); 234201468SrpauloMODULE_DEPEND(ohci, usb, 1, 1, 1); 235