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$"); 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; 105201468Srpaulo 106201468Srpaulo /* get all DMA memory */ 107201468Srpaulo if (usb_bus_mem_alloc_all(&sc->sc_bus, 108201468Srpaulo USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 109201468Srpaulo return (ENOMEM); 110201468Srpaulo } 111201468Srpaulo 112201468Srpaulo sc->sc_bus.usbrev = USB_REV_2_0; 113201468Srpaulo 114201468Srpaulo rid = 0; 115201468Srpaulo sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, 116201468Srpaulo &rid, RF_ACTIVE); 117201468Srpaulo if (!sc->sc_io_res) { 118201468Srpaulo device_printf(self, "Could not map memory\n"); 119201468Srpaulo goto error; 120201468Srpaulo } 121201468Srpaulo sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 122201468Srpaulo bsh = rman_get_bushandle(sc->sc_io_res); 123201468Srpaulo 124201468Srpaulo /*magic, undocumented initialization*/ 125201468Srpaulo bus_space_write_4((sc)->sc_io_tag, bsh, 0x04, 0x106); 126201468Srpaulo 127201468Srpaulo bus_space_write_4((sc)->sc_io_tag, bsh, 0x40, (3 << 5)|0x2000); 128201468Srpaulo 129201468Srpaulo DELAY(1000); 130201468Srpaulo 131201468Srpaulo sc->sc_io_size = 4096; 132201468Srpaulo 133201468Srpaulo if (bus_space_subregion(sc->sc_io_tag, bsh, 0x4000000, 134201468Srpaulo sc->sc_io_size, &sc->sc_io_hdl) != 0) 135201468Srpaulo panic("%s: unable to subregion USB host registers", 136201468Srpaulo device_get_name(self)); 137201468Srpaulo 138201468Srpaulo rid = 0; 139201468Srpaulo sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 140201468Srpaulo RF_SHAREABLE | RF_ACTIVE); 141201468Srpaulo if (sc->sc_irq_res == NULL) { 142201468Srpaulo device_printf(self, "Could not allocate irq\n"); 143201468Srpaulo ehci_ebus_detach(self); 144201468Srpaulo return (ENXIO); 145201468Srpaulo } 146201468Srpaulo 147201468Srpaulo sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 148201468Srpaulo if (!sc->sc_bus.bdev) { 149201468Srpaulo device_printf(self, "Could not add USB device\n"); 150201468Srpaulo goto error; 151201468Srpaulo } 152201468Srpaulo device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 153201468Srpaulo device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 154201468Srpaulo 155201468Srpaulo sprintf(sc->sc_vendor, "Cavium"); 156201468Srpaulo 157201468Srpaulo err = bus_setup_intr(self,sc->sc_irq_res, 158201468Srpaulo INTR_TYPE_BIO | INTR_MPSAFE, NULL, 159201468Srpaulo (driver_intr_t *)ehci_interrupt, sc, 160201468Srpaulo &sc->sc_intr_hdl); 161201468Srpaulo if (err) { 162201468Srpaulo device_printf(self, "Could not setup error irq, %d\n", err); 163201468Srpaulo ih_err = NULL; 164201468Srpaulo goto error; 165201468Srpaulo } 166201468Srpaulo 167201468Srpaulo err = ehci_init(sc); 168201468Srpaulo if (!err) { 169201468Srpaulo err = device_probe_and_attach(sc->sc_bus.bdev); 170201468Srpaulo } 171201468Srpaulo if (err) { 172201468Srpaulo device_printf(self, "USB init failed err=%d\n", err); 173201468Srpaulo goto error; 174201468Srpaulo } 175201468Srpaulo return (0); 176201468Srpaulo 177201468Srpauloerror: 178201468Srpaulo ehci_ebus_detach(self); 179201468Srpaulo return (ENXIO); 180201468Srpaulo} 181201468Srpaulo 182201468Srpaulostatic int 183201468Srpauloehci_ebus_detach(device_t self) 184201468Srpaulo{ 185201468Srpaulo ehci_softc_t *sc = device_get_softc(self); 186201468Srpaulo device_t bdev; 187201468Srpaulo int err; 188201468Srpaulo 189201468Srpaulo if (sc->sc_bus.bdev) { 190201468Srpaulo bdev = sc->sc_bus.bdev; 191201468Srpaulo device_detach(bdev); 192201468Srpaulo device_delete_child(self, bdev); 193201468Srpaulo } 194201468Srpaulo /* during module unload there are lots of children leftover */ 195229118Shselasky device_delete_children(self); 196201468Srpaulo 197201468Srpaulo /* 198220558Shselasky * disable interrupts that might have been switched on in 199220558Shselasky * ehci_ebus_attach() 200201468Srpaulo */ 201201468Srpaulo if (sc->sc_io_res) { 202201468Srpaulo EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); 203201468Srpaulo } 204201468Srpaulo if (sc->sc_irq_res && sc->sc_intr_hdl) { 205201468Srpaulo /* 206201468Srpaulo * only call ehci_detach() after ehci_init() 207201468Srpaulo */ 208201468Srpaulo ehci_detach(sc); 209201468Srpaulo 210201468Srpaulo err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 211201468Srpaulo 212201468Srpaulo if (err) 213201468Srpaulo /* XXX or should we panic? */ 214201468Srpaulo device_printf(self, "Could not tear down irq, %d\n", 215201468Srpaulo err); 216201468Srpaulo sc->sc_intr_hdl = NULL; 217201468Srpaulo } 218201468Srpaulo if (sc->sc_irq_res) { 219201468Srpaulo bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); 220201468Srpaulo sc->sc_irq_res = NULL; 221201468Srpaulo } 222201468Srpaulo if (sc->sc_io_res) { 223201468Srpaulo bus_release_resource(self, SYS_RES_MEMORY, 0, 224201468Srpaulo sc->sc_io_res); 225201468Srpaulo sc->sc_io_res = NULL; 226201468Srpaulo } 227201468Srpaulo usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 228201468Srpaulo 229201468Srpaulo return (0); 230201468Srpaulo} 231201468Srpaulo 232201468Srpaulostatic device_method_t ehci_methods[] = { 233201468Srpaulo /* Device interface */ 234201468Srpaulo DEVMETHOD(device_probe, ehci_ebus_probe), 235201468Srpaulo DEVMETHOD(device_attach, ehci_ebus_attach), 236201468Srpaulo DEVMETHOD(device_detach, ehci_ebus_detach), 237229096Shselasky DEVMETHOD(device_suspend, bus_generic_suspend), 238229096Shselasky DEVMETHOD(device_resume, bus_generic_resume), 239229096Shselasky DEVMETHOD(device_shutdown, bus_generic_shutdown), 240201468Srpaulo 241229093Shselasky DEVMETHOD_END 242201468Srpaulo}; 243201468Srpaulo 244201468Srpaulostatic driver_t ehci_driver = { 245229096Shselasky .name = "ehci", 246229096Shselasky .methods = ehci_methods, 247229096Shselasky .size = sizeof(ehci_softc_t), 248201468Srpaulo}; 249201468Srpaulo 250201468Srpaulostatic devclass_t ehci_devclass; 251201468Srpaulo 252201468SrpauloDRIVER_MODULE(ehci, econaarm, ehci_driver, ehci_devclass, 0, 0); 253201468SrpauloMODULE_DEPEND(ehci, usb, 1, 1, 1); 254