octusb_octeon.c revision 229168
1210312Sjmallett#include <sys/cdefs.h> 2210312Sjmallett__FBSDID("$FreeBSD: head/sys/mips/cavium/usb/octusb_octeon.c 229168 2012-01-01 09:12:21Z gonzo $"); 3210312Sjmallett 4210312Sjmallett/*- 5210312Sjmallett * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved. 6210312Sjmallett * 7210312Sjmallett * Redistribution and use in source and binary forms, with or without 8210312Sjmallett * modification, are permitted provided that the following conditions 9210312Sjmallett * are met: 10210312Sjmallett * 1. Redistributions of source code must retain the above copyright 11210312Sjmallett * notice, this list of conditions and the following disclaimer. 12210312Sjmallett * 2. Redistributions in binary form must reproduce the above copyright 13210312Sjmallett * notice, this list of conditions and the following disclaimer in the 14210312Sjmallett * documentation and/or other materials provided with the distribution. 15210312Sjmallett * 16210312Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17210312Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18210312Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19210312Sjmallett * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20210312Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21210312Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22210312Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23210312Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24210312Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25210312Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26210312Sjmallett * SUCH DAMAGE. 27210312Sjmallett */ 28210312Sjmallett 29210312Sjmallett#include <sys/stdint.h> 30210312Sjmallett#include <sys/stddef.h> 31210312Sjmallett#include <sys/param.h> 32210312Sjmallett#include <sys/queue.h> 33210312Sjmallett#include <sys/types.h> 34210312Sjmallett#include <sys/systm.h> 35210312Sjmallett#include <sys/kernel.h> 36210312Sjmallett#include <sys/bus.h> 37210312Sjmallett#include <sys/module.h> 38210312Sjmallett#include <sys/lock.h> 39210312Sjmallett#include <sys/mutex.h> 40210312Sjmallett#include <sys/condvar.h> 41210312Sjmallett#include <sys/sysctl.h> 42210312Sjmallett#include <sys/sx.h> 43210312Sjmallett#include <sys/unistd.h> 44210312Sjmallett#include <sys/callout.h> 45210312Sjmallett#include <sys/malloc.h> 46210312Sjmallett#include <sys/priv.h> 47210312Sjmallett#include <sys/rman.h> 48210312Sjmallett 49210312Sjmallett#include <dev/usb/usb.h> 50210312Sjmallett#include <dev/usb/usbdi.h> 51210312Sjmallett 52210312Sjmallett#include <dev/usb/usb_core.h> 53210312Sjmallett#include <dev/usb/usb_busdma.h> 54210312Sjmallett#include <dev/usb/usb_process.h> 55210312Sjmallett#include <dev/usb/usb_util.h> 56210312Sjmallett 57210312Sjmallett#include <dev/usb/usb_controller.h> 58210312Sjmallett#include <dev/usb/usb_bus.h> 59210312Sjmallett 60210312Sjmallett#include <contrib/octeon-sdk/cvmx.h> 61210312Sjmallett#include <contrib/octeon-sdk/cvmx-interrupt.h> 62210312Sjmallett#include <contrib/octeon-sdk/cvmx-usb.h> 63210312Sjmallett 64210312Sjmallett#include <mips/cavium/usb/octusb.h> 65210312Sjmallett 66210312Sjmallett#define MEM_RID 0 67210312Sjmallett 68210312Sjmallettstatic device_identify_t octusb_octeon_identify; 69210312Sjmallettstatic device_probe_t octusb_octeon_probe; 70210312Sjmallettstatic device_attach_t octusb_octeon_attach; 71210312Sjmallettstatic device_detach_t octusb_octeon_detach; 72210312Sjmallett 73210312Sjmallettstruct octusb_octeon_softc { 74210312Sjmallett struct octusb_softc sc_dci; /* must be first */ 75210312Sjmallett}; 76210312Sjmallett 77210312Sjmallettstatic void 78210312Sjmallettoctusb_octeon_identify(driver_t *drv, device_t parent) 79210312Sjmallett{ 80210312Sjmallett if (octeon_has_feature(OCTEON_FEATURE_USB)) 81210312Sjmallett BUS_ADD_CHILD(parent, 0, "octusb", 0); 82210312Sjmallett} 83210312Sjmallett 84210312Sjmallettstatic int 85210312Sjmallettoctusb_octeon_probe(device_t dev) 86210312Sjmallett{ 87210312Sjmallett device_set_desc(dev, "Cavium Octeon USB controller"); 88210312Sjmallett return (0); 89210312Sjmallett} 90210312Sjmallett 91210312Sjmallettstatic int 92210312Sjmallettoctusb_octeon_attach(device_t dev) 93210312Sjmallett{ 94210312Sjmallett struct octusb_octeon_softc *sc = device_get_softc(dev); 95210312Sjmallett int err; 96210312Sjmallett int rid; 97229168Sgonzo int nports; 98229168Sgonzo int i; 99210312Sjmallett 100210312Sjmallett /* setup controller interface softc */ 101210312Sjmallett 102210312Sjmallett /* initialise some bus fields */ 103210312Sjmallett sc->sc_dci.sc_bus.parent = dev; 104210312Sjmallett sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices; 105210312Sjmallett sc->sc_dci.sc_bus.devices_max = OCTUSB_MAX_DEVICES; 106210312Sjmallett 107210312Sjmallett /* get all DMA memory */ 108210312Sjmallett if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus, 109210312Sjmallett USB_GET_DMA_TAG(dev), NULL)) { 110210312Sjmallett return (ENOMEM); 111210312Sjmallett } 112229168Sgonzo nports = cvmx_usb_get_num_ports(); 113229168Sgonzo if (nports > OCTUSB_MAX_PORTS) 114229168Sgonzo panic("octusb: too many USB ports %d", nports); 115229168Sgonzo for (i = 0; i < nports; i++) { 116229168Sgonzo rid = 0; 117229168Sgonzo sc->sc_dci.sc_irq_res[i] = 118229168Sgonzo bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 119229168Sgonzo CVMX_IRQ_USB0 + i, CVMX_IRQ_USB0 + i, 1, RF_ACTIVE); 120229168Sgonzo if (!(sc->sc_dci.sc_irq_res[i])) { 121229168Sgonzo goto error; 122229168Sgonzo } 123229168Sgonzo 124229168Sgonzo#if (__FreeBSD_version >= 700031) 125229168Sgonzo err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res[i], INTR_TYPE_BIO | INTR_MPSAFE, 126229168Sgonzo NULL, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl[i]); 127229168Sgonzo#else 128229168Sgonzo err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res[i], INTR_TYPE_BIO | INTR_MPSAFE, 129229168Sgonzo (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl[i]); 130229168Sgonzo#endif 131229168Sgonzo if (err) { 132229168Sgonzo sc->sc_dci.sc_intr_hdl[i] = NULL; 133229168Sgonzo goto error; 134229168Sgonzo } 135210312Sjmallett } 136210312Sjmallett 137210312Sjmallett sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1); 138210312Sjmallett if (!(sc->sc_dci.sc_bus.bdev)) { 139210312Sjmallett goto error; 140210312Sjmallett } 141210312Sjmallett device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus); 142210312Sjmallett 143229168Sgonzo 144210312Sjmallett err = octusb_init(&sc->sc_dci); 145210312Sjmallett if (!err) { 146210312Sjmallett err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev); 147210312Sjmallett } 148210312Sjmallett if (err) { 149210312Sjmallett goto error; 150210312Sjmallett } 151210312Sjmallett return (0); 152210312Sjmallett 153210312Sjmalletterror: 154210312Sjmallett octusb_octeon_detach(dev); 155210312Sjmallett return (ENXIO); 156210312Sjmallett} 157210312Sjmallett 158210312Sjmallettstatic int 159210312Sjmallettoctusb_octeon_detach(device_t dev) 160210312Sjmallett{ 161210312Sjmallett struct octusb_octeon_softc *sc = device_get_softc(dev); 162210312Sjmallett device_t bdev; 163210312Sjmallett int err; 164229168Sgonzo int nports; 165229168Sgonzo int i; 166210312Sjmallett 167210312Sjmallett if (sc->sc_dci.sc_bus.bdev) { 168210312Sjmallett bdev = sc->sc_dci.sc_bus.bdev; 169210312Sjmallett device_detach(bdev); 170210312Sjmallett device_delete_child(dev, bdev); 171210312Sjmallett } 172210312Sjmallett /* during module unload there are lots of children leftover */ 173227849Shselasky device_delete_children(dev); 174210312Sjmallett 175229168Sgonzo if (sc->sc_dci.sc_irq_res[0] && sc->sc_dci.sc_intr_hdl[0]) 176210312Sjmallett /* 177229168Sgonzo * only call octusb_octeon_uninit() after octusb_octeon_init() 178229168Sgonzo */ 179210312Sjmallett octusb_uninit(&sc->sc_dci); 180210312Sjmallett 181229168Sgonzo nports = cvmx_usb_get_num_ports(); 182229168Sgonzo if (nports > OCTUSB_MAX_PORTS) 183229168Sgonzo panic("octusb: too many USB ports %d", nports); 184229168Sgonzo for (i = 0; i < nports; i++) { 185229168Sgonzo if (sc->sc_dci.sc_irq_res[0] && sc->sc_dci.sc_intr_hdl[0]) { 186229168Sgonzo err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res[i], 187229168Sgonzo sc->sc_dci.sc_intr_hdl[i]); 188229168Sgonzo sc->sc_dci.sc_intr_hdl[i] = NULL; 189229168Sgonzo } 190229168Sgonzo if (sc->sc_dci.sc_irq_res) { 191229168Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, 192229168Sgonzo sc->sc_dci.sc_irq_res[i]); 193229168Sgonzo sc->sc_dci.sc_irq_res[i] = NULL; 194229168Sgonzo } 195210312Sjmallett } 196210312Sjmallett usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL); 197210312Sjmallett 198210312Sjmallett return (0); 199210312Sjmallett} 200210312Sjmallett 201210312Sjmallettstatic device_method_t octusb_octeon_methods[] = { 202210312Sjmallett /* Device interface */ 203210312Sjmallett DEVMETHOD(device_identify, octusb_octeon_identify), 204210312Sjmallett DEVMETHOD(device_probe, octusb_octeon_probe), 205210312Sjmallett DEVMETHOD(device_attach, octusb_octeon_attach), 206210312Sjmallett DEVMETHOD(device_detach, octusb_octeon_detach), 207228483Shselasky DEVMETHOD(device_resume, bus_generic_resume), 208228483Shselasky DEVMETHOD(device_suspend, bus_generic_suspend), 209228483Shselasky DEVMETHOD(device_shutdown, bus_generic_shutdown), 210210312Sjmallett 211227843Smarius DEVMETHOD_END 212210312Sjmallett}; 213210312Sjmallett 214210312Sjmallettstatic driver_t octusb_octeon_driver = { 215228483Shselasky .name = "octusb", 216228483Shselasky .methods = octusb_octeon_methods, 217228483Shselasky .size = sizeof(struct octusb_octeon_softc), 218210312Sjmallett}; 219210312Sjmallett 220210312Sjmallettstatic devclass_t octusb_octeon_devclass; 221210312Sjmallett 222210312SjmallettDRIVER_MODULE(octusb, ciu, octusb_octeon_driver, octusb_octeon_devclass, 0, 0); 223