ohci_ec.c revision 278278
1258945Sroberto/*- 2280849Scy * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com> 3258945Sroberto * All rights reserved. 4258945Sroberto * 5258945Sroberto * Redistribution and use in source and binary forms, with or without 6258945Sroberto * modification, are permitted provided that the following conditions 7258945Sroberto * are met: 8258945Sroberto * 1. Redistributions of source code must retain the above copyright 9258945Sroberto * notice, this list of conditions and the following disclaimer. 10258945Sroberto * 2. Redistributions in binary form must reproduce the above copyright 11258945Sroberto * notice, this list of conditions and the following disclaimer in the 12258945Sroberto * documentation and/or other materials provided with the distribution. 13258945Sroberto * 14258945Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15258945Sroberto * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16258945Sroberto * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17258945Sroberto * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18280849Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19258945Sroberto * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20258945Sroberto * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21258945Sroberto * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22258945Sroberto * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23258945Sroberto * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24258945Sroberto */ 25258945Sroberto 26258945Sroberto#include <sys/cdefs.h> 27258945Sroberto__FBSDID("$FreeBSD: stable/10/sys/arm/cavium/cns11xx/ohci_ec.c 278278 2015-02-05 20:03:02Z hselasky $"); 28258945Sroberto 29258945Sroberto#include <sys/stdint.h> 30258945Sroberto#include <sys/stddef.h> 31258945Sroberto#include <sys/param.h> 32258945Sroberto#include <sys/queue.h> 33258945Sroberto#include <sys/types.h> 34258945Sroberto#include <sys/systm.h> 35258945Sroberto#include <sys/kernel.h> 36258945Sroberto#include <sys/bus.h> 37258945Sroberto#include <sys/module.h> 38258945Sroberto#include <sys/lock.h> 39258945Sroberto#include <sys/mutex.h> 40258945Sroberto#include <sys/condvar.h> 41258945Sroberto#include <sys/sysctl.h> 42258945Sroberto#include <sys/sx.h> 43258945Sroberto#include <sys/unistd.h> 44258945Sroberto#include <sys/callout.h> 45258945Sroberto#include <sys/malloc.h> 46258945Sroberto#include <sys/priv.h> 47258945Sroberto 48258945Sroberto#include <dev/usb/usb.h> 49258945Sroberto#include <dev/usb/usbdi.h> 50258945Sroberto 51258945Sroberto#include <dev/usb/usb_core.h> 52258945Sroberto#include <dev/usb/usb_busdma.h> 53258945Sroberto#include <dev/usb/usb_process.h> 54258945Sroberto#include <dev/usb/usb_util.h> 55258945Sroberto 56258945Sroberto#include <dev/usb/usb_controller.h> 57258945Sroberto#include <dev/usb/usb_bus.h> 58258945Sroberto#include <dev/usb/controller/ohci.h> 59258945Sroberto#include <dev/usb/controller/ohcireg.h> 60258945Sroberto 61258945Sroberto#include <sys/rman.h> 62258945Sroberto 63258945Sroberto#include <arm/cavium/cns11xx/econa_reg.h> 64258945Sroberto 65258945Sroberto#define MEM_RID 0 66258945Sroberto 67258945Srobertostatic device_probe_t ohci_ec_probe; 68258945Srobertostatic device_attach_t ohci_ec_attach; 69258945Srobertostatic device_detach_t ohci_ec_detach; 70258945Sroberto 71258945Srobertostruct ec_ohci_softc { 72258945Sroberto struct ohci_softc sc_ohci; /* must be first */ 73258945Sroberto}; 74258945Sroberto 75258945Srobertostatic int 76258945Srobertoohci_ec_probe(device_t dev) 77258945Sroberto{ 78258945Sroberto device_set_desc(dev, "Econa integrated OHCI controller"); 79258945Sroberto return (BUS_PROBE_DEFAULT); 80258945Sroberto} 81258945Sroberto 82258945Srobertostatic int 83258945Srobertoohci_ec_attach(device_t dev) 84258945Sroberto{ 85258945Sroberto struct ec_ohci_softc *sc = device_get_softc(dev); 86258945Sroberto bus_space_handle_t bsh; 87258945Sroberto int err; 88280849Scy int rid; 89258945Sroberto 90258945Sroberto /* initialise some bus fields */ 91258945Sroberto sc->sc_ohci.sc_bus.parent = dev; 92258945Sroberto sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices; 93258945Sroberto sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES; 94280849Scy sc->sc_ohci.sc_bus.dma_bits = 32; 95258945Sroberto 96258945Sroberto /* get all DMA memory */ 97258945Sroberto if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, 98258945Sroberto USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { 99258945Sroberto return (ENOMEM); 100258945Sroberto } 101258945Sroberto sc->sc_ohci.sc_dev = dev; 102258945Sroberto 103258945Sroberto rid = MEM_RID; 104258945Sroberto 105258945Sroberto sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 106258945Sroberto &rid, RF_ACTIVE); 107258945Sroberto 108258945Sroberto if (!(sc->sc_ohci.sc_io_res)) { 109258945Sroberto err = ENOMEM; 110258945Sroberto goto error; 111258945Sroberto } 112258945Sroberto sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); 113258945Sroberto bsh = rman_get_bushandle(sc->sc_ohci.sc_io_res); 114258945Sroberto /* Undocumented magic initialization */ 115258945Sroberto bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x04, 0x146); 116258945Sroberto 117258945Sroberto bus_space_write_4((sc)->sc_ohci.sc_io_tag, bsh,0x44, 0x0200); 118258945Sroberto 119258945Sroberto DELAY(1000); 120258945Sroberto 121258945Sroberto sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); 122258945Sroberto 123258945Sroberto if (bus_space_subregion(sc->sc_ohci.sc_io_tag, bsh, 0x4000000, 124258945Sroberto sc->sc_ohci.sc_io_size, &sc->sc_ohci.sc_io_hdl) != 0) 125258945Sroberto panic("%s: unable to subregion USB host registers", 126258945Sroberto device_get_name(dev)); 127258945Sroberto 128258945Sroberto rid = 0; 129258945Sroberto sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 130258945Sroberto RF_ACTIVE); 131258945Sroberto if (!(sc->sc_ohci.sc_irq_res)) { 132258945Sroberto goto error; 133258945Sroberto } 134258945Sroberto sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); 135258945Sroberto if (!(sc->sc_ohci.sc_bus.bdev)) { 136258945Sroberto goto error; 137258945Sroberto } 138258945Sroberto device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); 139258945Sroberto 140258945Sroberto strlcpy(sc->sc_ohci.sc_vendor, "Cavium", 141258945Sroberto sizeof(sc->sc_ohci.sc_vendor)); 142258945Sroberto 143258945Sroberto#if (__FreeBSD_version >= 700031) 144258945Sroberto err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 145258945Sroberto INTR_TYPE_BIO | INTR_MPSAFE, NULL, 146258945Sroberto (driver_intr_t *)ohci_interrupt, sc, 147258945Sroberto &sc->sc_ohci.sc_intr_hdl); 148258945Sroberto#else 149258945Sroberto err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, 150258945Sroberto INTR_TYPE_BIO | INTR_MPSAFE, 151258945Sroberto (driver_intr_t *)ohci_interrupt, sc, 152258945Sroberto &sc->sc_ohci.sc_intr_hdl); 153258945Sroberto#endif 154258945Sroberto if (err) { 155258945Sroberto sc->sc_ohci.sc_intr_hdl = NULL; 156258945Sroberto goto error; 157258945Sroberto } 158258945Sroberto 159258945Sroberto bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 160258945Sroberto OHCI_CONTROL, 0); 161258945Sroberto 162258945Sroberto err = ohci_init(&sc->sc_ohci); 163258945Sroberto if (!err) { 164258945Sroberto err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); 165258945Sroberto } 166258945Sroberto if (err) { 167258945Sroberto goto error; 168258945Sroberto } 169258945Sroberto return (0); 170258945Sroberto 171258945Srobertoerror: 172258945Sroberto ohci_ec_detach(dev); 173258945Sroberto return (ENXIO); 174258945Sroberto} 175258945Sroberto 176258945Srobertostatic int 177258945Srobertoohci_ec_detach(device_t dev) 178258945Sroberto{ 179258945Sroberto struct ec_ohci_softc *sc = device_get_softc(dev); 180258945Sroberto device_t bdev; 181258945Sroberto int err; 182258945Sroberto 183258945Sroberto if (sc->sc_ohci.sc_bus.bdev) { 184258945Sroberto bdev = sc->sc_ohci.sc_bus.bdev; 185258945Sroberto device_detach(bdev); 186258945Sroberto device_delete_child(dev, bdev); 187258945Sroberto } 188258945Sroberto /* during module unload there are lots of children leftover */ 189258945Sroberto device_delete_children(dev); 190258945Sroberto 191258945Sroberto bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, 192258945Sroberto OHCI_CONTROL, 0); 193258945Sroberto 194258945Sroberto if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { 195258945Sroberto /* 196258945Sroberto * only call ohci_detach() after ohci_init() 197258945Sroberto */ 198258945Sroberto ohci_detach(&sc->sc_ohci); 199258945Sroberto 200258945Sroberto err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, 201258945Sroberto sc->sc_ohci.sc_intr_hdl); 202258945Sroberto sc->sc_ohci.sc_intr_hdl = NULL; 203258945Sroberto } 204258945Sroberto if (sc->sc_ohci.sc_irq_res) { 205258945Sroberto bus_release_resource(dev, SYS_RES_IRQ, 0, 206258945Sroberto sc->sc_ohci.sc_irq_res); 207258945Sroberto sc->sc_ohci.sc_irq_res = NULL; 208258945Sroberto } 209258945Sroberto if (sc->sc_ohci.sc_io_res) { 210258945Sroberto bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, 211258945Sroberto sc->sc_ohci.sc_io_res); 212258945Sroberto sc->sc_ohci.sc_io_res = NULL; 213258945Sroberto } 214258945Sroberto usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); 215258945Sroberto 216258945Sroberto return (0); 217258945Sroberto} 218258945Sroberto 219258945Srobertostatic device_method_t ohci_methods[] = { 220258945Sroberto /* Device interface */ 221258945Sroberto DEVMETHOD(device_probe, ohci_ec_probe), 222280849Scy DEVMETHOD(device_attach, ohci_ec_attach), 223258945Sroberto DEVMETHOD(device_detach, ohci_ec_detach), 224258945Sroberto DEVMETHOD(device_resume, bus_generic_resume), 225258945Sroberto DEVMETHOD(device_suspend, bus_generic_suspend), 226258945Sroberto DEVMETHOD(device_shutdown, bus_generic_shutdown), 227258945Sroberto 228258945Sroberto DEVMETHOD_END 229258945Sroberto}; 230258945Sroberto 231258945Srobertostatic driver_t ohci_driver = { 232258945Sroberto .name = "ohci", 233258945Sroberto .methods = ohci_methods, 234258945Sroberto .size = sizeof(struct ec_ohci_softc), 235258945Sroberto}; 236258945Sroberto 237258945Srobertostatic devclass_t ohci_devclass; 238258945Sroberto 239258945SrobertoDRIVER_MODULE(ohci, econaarm, ohci_driver, ohci_devclass, 0, 0); 240258945SrobertoMODULE_DEPEND(ohci, usb, 1, 1, 1); 241258945Sroberto