1201468Srpaulo/*- 2201468Srpaulo * Copyright (C) 2009 Yohanes Nugroho <yohanes@gmail.com> 3201468Srpaulo * based on ehci_mbus.c 4201468Srpaulo * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. 5201468Srpaulo * All rights reserved. 6201468Srpaulo * 7201468Srpaulo * 8201468Srpaulo * Redistribution and use in source and binary forms, with or without 9201468Srpaulo * modification, are permitted provided that the following conditions 10201468Srpaulo * are met: 11201468Srpaulo * 1. Redistributions of source code must retain the above copyright 12201468Srpaulo * notice, this list of conditions and the following disclaimer. 13201468Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 14201468Srpaulo * notice, this list of conditions and the following disclaimer in the 15201468Srpaulo * documentation and/or other materials provided with the distribution. 16201468Srpaulo * 3. Neither the name of MARVELL nor the names of contributors 17201468Srpaulo * may be used to endorse or promote products derived from this software 18201468Srpaulo * without specific prior written permission. 19201468Srpaulo * 20201468Srpaulo * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21201468Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22201468Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23201468Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 24201468Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25201468Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26201468Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27201468Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28201468Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29201468Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30201468Srpaulo * SUCH DAMAGE. 31201468Srpaulo */ 32201468Srpaulo 33201468Srpaulo 34201468Srpaulo#include <sys/cdefs.h> 35201468Srpaulo__FBSDID("$FreeBSD: releng/10.3/sys/arm/cavium/cns11xx/ehci_ebus.c 278278 2015-02-05 20:03:02Z hselasky $"); 36201468Srpaulo 37201468Srpaulo#include "opt_bus.h" 38201468Srpaulo 39201468Srpaulo#include <machine/resource.h> 40201468Srpaulo 41201468Srpaulo#include <sys/stdint.h> 42201468Srpaulo#include <sys/stddef.h> 43201468Srpaulo#include <sys/param.h> 44201468Srpaulo#include <sys/queue.h> 45201468Srpaulo#include <sys/types.h> 46201468Srpaulo#include <sys/systm.h> 47201468Srpaulo#include <sys/kernel.h> 48201468Srpaulo#include <sys/bus.h> 49201468Srpaulo#include <sys/module.h> 50201468Srpaulo#include <sys/lock.h> 51201468Srpaulo#include <sys/mutex.h> 52201468Srpaulo#include <sys/condvar.h> 53201468Srpaulo#include <sys/sysctl.h> 54201468Srpaulo#include <sys/sx.h> 55201468Srpaulo#include <sys/unistd.h> 56201468Srpaulo#include <sys/callout.h> 57201468Srpaulo#include <sys/malloc.h> 58201468Srpaulo#include <sys/priv.h> 59201468Srpaulo 60201468Srpaulo#include <sys/rman.h> 61201468Srpaulo 62201468Srpaulo#include <dev/usb/usb.h> 63201468Srpaulo#include <dev/usb/usbdi.h> 64201468Srpaulo 65201468Srpaulo#include <dev/usb/usb_core.h> 66201468Srpaulo#include <dev/usb/usb_busdma.h> 67201468Srpaulo#include <dev/usb/usb_process.h> 68201468Srpaulo#include <dev/usb/usb_util.h> 69201468Srpaulo 70201468Srpaulo#include <dev/usb/usb_controller.h> 71201468Srpaulo#include <dev/usb/usb_bus.h> 72201468Srpaulo#include <dev/usb/controller/ehci.h> 73201468Srpaulo#include <dev/usb/controller/ehcireg.h> 74201468Srpaulo 75201468Srpaulo 76201468Srpaulostatic device_attach_t ehci_ebus_attach; 77201468Srpaulostatic device_detach_t ehci_ebus_detach; 78201468Srpaulo 79201468Srpaulostatic void *ih_err; 80201468Srpaulo 81201468Srpaulo#define EHCI_HC_DEVSTR "CNS11XX USB EHCI" 82201468Srpaulo#define USB_BRIDGE_INTR_MASK 0x214 83201468Srpaulo 84201468Srpaulostatic int 85201468Srpauloehci_ebus_probe(device_t self) 86201468Srpaulo{ 87201468Srpaulo 88201468Srpaulo device_set_desc(self, EHCI_HC_DEVSTR); 89201468Srpaulo 90201468Srpaulo return (BUS_PROBE_DEFAULT); 91201468Srpaulo} 92201468Srpaulo 93201468Srpaulostatic int 94201468Srpauloehci_ebus_attach(device_t self) 95201468Srpaulo{ 96201468Srpaulo ehci_softc_t *sc = device_get_softc(self); 97201468Srpaulo bus_space_handle_t bsh; 98201468Srpaulo int err; 99201468Srpaulo int rid; 100201468Srpaulo 101201468Srpaulo /* initialise some bus fields */ 102201468Srpaulo sc->sc_bus.parent = self; 103201468Srpaulo sc->sc_bus.devices = sc->sc_devices; 104201468Srpaulo sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 105278278Shselasky sc->sc_bus.dma_bits = 32; 106201468Srpaulo 107201468Srpaulo /* get all DMA memory */ 108201468Srpaulo if (usb_bus_mem_alloc_all(&sc->sc_bus, 109201468Srpaulo USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 110201468Srpaulo return (ENOMEM); 111201468Srpaulo } 112201468Srpaulo 113201468Srpaulo sc->sc_bus.usbrev = USB_REV_2_0; 114201468Srpaulo 115201468Srpaulo rid = 0; 116201468Srpaulo sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, 117201468Srpaulo &rid, RF_ACTIVE); 118201468Srpaulo if (!sc->sc_io_res) { 119201468Srpaulo device_printf(self, "Could not map memory\n"); 120201468Srpaulo goto error; 121201468Srpaulo } 122201468Srpaulo sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 123201468Srpaulo bsh = rman_get_bushandle(sc->sc_io_res); 124201468Srpaulo 125201468Srpaulo /*magic, undocumented initialization*/ 126201468Srpaulo bus_space_write_4((sc)->sc_io_tag, bsh, 0x04, 0x106); 127201468Srpaulo 128201468Srpaulo bus_space_write_4((sc)->sc_io_tag, bsh, 0x40, (3 << 5)|0x2000); 129201468Srpaulo 130201468Srpaulo DELAY(1000); 131201468Srpaulo 132201468Srpaulo sc->sc_io_size = 4096; 133201468Srpaulo 134201468Srpaulo if (bus_space_subregion(sc->sc_io_tag, bsh, 0x4000000, 135201468Srpaulo sc->sc_io_size, &sc->sc_io_hdl) != 0) 136201468Srpaulo panic("%s: unable to subregion USB host registers", 137201468Srpaulo device_get_name(self)); 138201468Srpaulo 139201468Srpaulo rid = 0; 140201468Srpaulo sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 141201468Srpaulo RF_SHAREABLE | RF_ACTIVE); 142201468Srpaulo if (sc->sc_irq_res == NULL) { 143201468Srpaulo device_printf(self, "Could not allocate irq\n"); 144201468Srpaulo ehci_ebus_detach(self); 145201468Srpaulo return (ENXIO); 146201468Srpaulo } 147201468Srpaulo 148201468Srpaulo sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 149201468Srpaulo if (!sc->sc_bus.bdev) { 150201468Srpaulo device_printf(self, "Could not add USB device\n"); 151201468Srpaulo goto error; 152201468Srpaulo } 153201468Srpaulo device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 154201468Srpaulo device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 155201468Srpaulo 156201468Srpaulo sprintf(sc->sc_vendor, "Cavium"); 157201468Srpaulo 158201468Srpaulo err = bus_setup_intr(self,sc->sc_irq_res, 159201468Srpaulo INTR_TYPE_BIO | INTR_MPSAFE, NULL, 160201468Srpaulo (driver_intr_t *)ehci_interrupt, sc, 161201468Srpaulo &sc->sc_intr_hdl); 162201468Srpaulo if (err) { 163201468Srpaulo device_printf(self, "Could not setup error irq, %d\n", err); 164201468Srpaulo ih_err = NULL; 165201468Srpaulo goto error; 166201468Srpaulo } 167201468Srpaulo 168201468Srpaulo err = ehci_init(sc); 169201468Srpaulo if (!err) { 170201468Srpaulo err = device_probe_and_attach(sc->sc_bus.bdev); 171201468Srpaulo } 172201468Srpaulo if (err) { 173201468Srpaulo device_printf(self, "USB init failed err=%d\n", err); 174201468Srpaulo goto error; 175201468Srpaulo } 176201468Srpaulo return (0); 177201468Srpaulo 178201468Srpauloerror: 179201468Srpaulo ehci_ebus_detach(self); 180201468Srpaulo return (ENXIO); 181201468Srpaulo} 182201468Srpaulo 183201468Srpaulostatic int 184201468Srpauloehci_ebus_detach(device_t self) 185201468Srpaulo{ 186201468Srpaulo ehci_softc_t *sc = device_get_softc(self); 187201468Srpaulo device_t bdev; 188201468Srpaulo int err; 189201468Srpaulo 190201468Srpaulo if (sc->sc_bus.bdev) { 191201468Srpaulo bdev = sc->sc_bus.bdev; 192201468Srpaulo device_detach(bdev); 193201468Srpaulo device_delete_child(self, bdev); 194201468Srpaulo } 195201468Srpaulo /* during module unload there are lots of children leftover */ 196227849Shselasky device_delete_children(self); 197201468Srpaulo 198201468Srpaulo /* 199220558Shselasky * disable interrupts that might have been switched on in 200220558Shselasky * ehci_ebus_attach() 201201468Srpaulo */ 202201468Srpaulo if (sc->sc_io_res) { 203201468Srpaulo EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); 204201468Srpaulo } 205201468Srpaulo if (sc->sc_irq_res && sc->sc_intr_hdl) { 206201468Srpaulo /* 207201468Srpaulo * only call ehci_detach() after ehci_init() 208201468Srpaulo */ 209201468Srpaulo ehci_detach(sc); 210201468Srpaulo 211201468Srpaulo err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 212201468Srpaulo 213201468Srpaulo if (err) 214201468Srpaulo /* XXX or should we panic? */ 215201468Srpaulo device_printf(self, "Could not tear down irq, %d\n", 216201468Srpaulo err); 217201468Srpaulo sc->sc_intr_hdl = NULL; 218201468Srpaulo } 219201468Srpaulo if (sc->sc_irq_res) { 220201468Srpaulo bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); 221201468Srpaulo sc->sc_irq_res = NULL; 222201468Srpaulo } 223201468Srpaulo if (sc->sc_io_res) { 224201468Srpaulo bus_release_resource(self, SYS_RES_MEMORY, 0, 225201468Srpaulo sc->sc_io_res); 226201468Srpaulo sc->sc_io_res = NULL; 227201468Srpaulo } 228201468Srpaulo usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 229201468Srpaulo 230201468Srpaulo return (0); 231201468Srpaulo} 232201468Srpaulo 233201468Srpaulostatic device_method_t ehci_methods[] = { 234201468Srpaulo /* Device interface */ 235201468Srpaulo DEVMETHOD(device_probe, ehci_ebus_probe), 236201468Srpaulo DEVMETHOD(device_attach, ehci_ebus_attach), 237201468Srpaulo DEVMETHOD(device_detach, ehci_ebus_detach), 238228483Shselasky DEVMETHOD(device_suspend, bus_generic_suspend), 239228483Shselasky DEVMETHOD(device_resume, bus_generic_resume), 240228483Shselasky DEVMETHOD(device_shutdown, bus_generic_shutdown), 241201468Srpaulo 242227843Smarius DEVMETHOD_END 243201468Srpaulo}; 244201468Srpaulo 245201468Srpaulostatic driver_t ehci_driver = { 246228483Shselasky .name = "ehci", 247228483Shselasky .methods = ehci_methods, 248228483Shselasky .size = sizeof(ehci_softc_t), 249201468Srpaulo}; 250201468Srpaulo 251201468Srpaulostatic devclass_t ehci_devclass; 252201468Srpaulo 253201468SrpauloDRIVER_MODULE(ehci, econaarm, ehci_driver, ehci_devclass, 0, 0); 254201468SrpauloMODULE_DEPEND(ehci, usb, 1, 1, 1); 255