1230424Shselasky/*- 2230424Shselasky * Copyright (c) 2012 Hans Petter Selasky. All rights reserved. 3230424Shselasky * 4230424Shselasky * Redistribution and use in source and binary forms, with or without 5230424Shselasky * modification, are permitted provided that the following conditions 6230424Shselasky * are met: 7230424Shselasky * 1. Redistributions of source code must retain the above copyright 8230424Shselasky * notice, this list of conditions and the following disclaimer. 9230424Shselasky * 2. Redistributions in binary form must reproduce the above copyright 10230424Shselasky * notice, this list of conditions and the following disclaimer in the 11230424Shselasky * documentation and/or other materials provided with the distribution. 12230424Shselasky * 13230424Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14230424Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15230424Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16230424Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17230424Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18230424Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19230424Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20230424Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21230424Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22230424Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23230424Shselasky * SUCH DAMAGE. 24230424Shselasky */ 25230424Shselasky 26230424Shselasky#include <sys/cdefs.h> 27230424Shselasky__FBSDID("$FreeBSD$"); 28230424Shselasky 29230424Shselasky#include <sys/stdint.h> 30230424Shselasky#include <sys/stddef.h> 31230424Shselasky#include <sys/param.h> 32230424Shselasky#include <sys/queue.h> 33230424Shselasky#include <sys/types.h> 34230424Shselasky#include <sys/systm.h> 35230424Shselasky#include <sys/kernel.h> 36230424Shselasky#include <sys/bus.h> 37230424Shselasky#include <sys/module.h> 38230424Shselasky#include <sys/lock.h> 39230424Shselasky#include <sys/mutex.h> 40230424Shselasky#include <sys/condvar.h> 41230424Shselasky#include <sys/sysctl.h> 42230424Shselasky#include <sys/sx.h> 43230424Shselasky#include <sys/unistd.h> 44230424Shselasky#include <sys/callout.h> 45230424Shselasky#include <sys/malloc.h> 46230424Shselasky#include <sys/priv.h> 47232539Shselasky#include <sys/rman.h> 48230424Shselasky 49230424Shselasky#include <dev/usb/usb.h> 50230424Shselasky#include <dev/usb/usbdi.h> 51230424Shselasky 52230424Shselasky#include <dev/usb/usb_core.h> 53230424Shselasky#include <dev/usb/usb_busdma.h> 54230424Shselasky#include <dev/usb/usb_process.h> 55230424Shselasky#include <dev/usb/usb_util.h> 56230424Shselasky 57230424Shselasky#include <dev/usb/usb_controller.h> 58230424Shselasky#include <dev/usb/usb_bus.h> 59230424Shselasky 60230424Shselasky#include <dev/usb/controller/dwc_otg.h> 61230424Shselasky 62230424Shselaskystatic device_probe_t dwc_otg_probe; 63230424Shselaskystatic device_attach_t dwc_otg_attach; 64230424Shselaskystatic device_detach_t dwc_otg_detach; 65230424Shselasky 66230424Shselaskystruct dwc_otg_super_softc { 67230424Shselasky struct dwc_otg_softc sc_otg; /* must be first */ 68230424Shselasky}; 69230424Shselasky 70230424Shselaskystatic int 71230424Shselaskydwc_otg_probe(device_t dev) 72230424Shselasky{ 73230424Shselasky device_set_desc(dev, "DWC OTG 2.0 integrated USB controller"); 74230424Shselasky return (0); 75230424Shselasky} 76230424Shselasky 77230424Shselaskystatic int 78230424Shselaskydwc_otg_attach(device_t dev) 79230424Shselasky{ 80230424Shselasky struct dwc_otg_super_softc *sc = device_get_softc(dev); 81230424Shselasky int err; 82230424Shselasky int rid; 83230424Shselasky 84230424Shselasky /* initialise some bus fields */ 85230424Shselasky sc->sc_otg.sc_bus.parent = dev; 86230424Shselasky sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; 87230424Shselasky sc->sc_otg.sc_bus.devices_max = DWC_OTG_MAX_DEVICES; 88230424Shselasky 89230424Shselasky /* get all DMA memory */ 90230424Shselasky if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus, 91230424Shselasky USB_GET_DMA_TAG(dev), NULL)) { 92230424Shselasky return (ENOMEM); 93230424Shselasky } 94230424Shselasky rid = 0; 95230424Shselasky sc->sc_otg.sc_io_res = 96230424Shselasky bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 97230424Shselasky 98230424Shselasky if (!(sc->sc_otg.sc_io_res)) { 99230424Shselasky err = ENOMEM; 100230424Shselasky goto error; 101230424Shselasky } 102230424Shselasky sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); 103230424Shselasky sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); 104230424Shselasky sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res); 105230424Shselasky 106230424Shselasky rid = 0; 107230424Shselasky sc->sc_otg.sc_irq_res = 108230424Shselasky bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 109230424Shselasky if (sc->sc_otg.sc_irq_res == NULL) 110230424Shselasky goto error; 111230424Shselasky 112230424Shselasky sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); 113230424Shselasky if (sc->sc_otg.sc_bus.bdev == NULL) 114230424Shselasky goto error; 115230424Shselasky 116230424Shselasky device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); 117230424Shselasky 118230424Shselasky err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 119266575Shselasky &dwc_otg_filter_interrupt, &dwc_otg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); 120230424Shselasky if (err) { 121230424Shselasky sc->sc_otg.sc_intr_hdl = NULL; 122230424Shselasky goto error; 123230424Shselasky } 124230424Shselasky err = dwc_otg_init(&sc->sc_otg); 125230424Shselasky if (err == 0) { 126230424Shselasky err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); 127230424Shselasky } 128230424Shselasky if (err) 129230424Shselasky goto error; 130230424Shselasky return (0); 131230424Shselasky 132230424Shselaskyerror: 133230424Shselasky dwc_otg_detach(dev); 134230424Shselasky return (ENXIO); 135230424Shselasky} 136230424Shselasky 137230424Shselaskystatic int 138230424Shselaskydwc_otg_detach(device_t dev) 139230424Shselasky{ 140230424Shselasky struct dwc_otg_super_softc *sc = device_get_softc(dev); 141230424Shselasky device_t bdev; 142230424Shselasky int err; 143230424Shselasky 144230424Shselasky if (sc->sc_otg.sc_bus.bdev) { 145230424Shselasky bdev = sc->sc_otg.sc_bus.bdev; 146230424Shselasky device_detach(bdev); 147230424Shselasky device_delete_child(dev, bdev); 148230424Shselasky } 149230424Shselasky /* during module unload there are lots of children leftover */ 150230424Shselasky device_delete_children(dev); 151230424Shselasky 152230424Shselasky if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { 153230424Shselasky /* 154230424Shselasky * only call dwc_otg_uninit() after dwc_otg_init() 155230424Shselasky */ 156230424Shselasky dwc_otg_uninit(&sc->sc_otg); 157230424Shselasky 158230424Shselasky err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, 159230424Shselasky sc->sc_otg.sc_intr_hdl); 160230424Shselasky sc->sc_otg.sc_intr_hdl = NULL; 161230424Shselasky } 162230424Shselasky /* free IRQ channel, if any */ 163230424Shselasky if (sc->sc_otg.sc_irq_res) { 164230424Shselasky bus_release_resource(dev, SYS_RES_IRQ, 0, 165230424Shselasky sc->sc_otg.sc_irq_res); 166230424Shselasky sc->sc_otg.sc_irq_res = NULL; 167230424Shselasky } 168230424Shselasky /* free memory resource, if any */ 169230424Shselasky if (sc->sc_otg.sc_io_res) { 170230424Shselasky bus_release_resource(dev, SYS_RES_MEMORY, 0, 171230424Shselasky sc->sc_otg.sc_io_res); 172230424Shselasky sc->sc_otg.sc_io_res = NULL; 173230424Shselasky } 174230424Shselasky usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); 175230424Shselasky 176230424Shselasky return (0); 177230424Shselasky} 178230424Shselasky 179230424Shselaskystatic device_method_t dwc_otg_methods[] = { 180230424Shselasky /* Device interface */ 181230424Shselasky DEVMETHOD(device_probe, dwc_otg_probe), 182230424Shselasky DEVMETHOD(device_attach, dwc_otg_attach), 183230424Shselasky DEVMETHOD(device_detach, dwc_otg_detach), 184230424Shselasky DEVMETHOD(device_suspend, bus_generic_suspend), 185230424Shselasky DEVMETHOD(device_resume, bus_generic_resume), 186230424Shselasky DEVMETHOD(device_shutdown, bus_generic_shutdown), 187230424Shselasky 188230424Shselasky DEVMETHOD_END 189230424Shselasky}; 190230424Shselasky 191230424Shselaskystatic driver_t dwc_otg_driver = { 192230424Shselasky .name = "dwc_otg", 193230424Shselasky .methods = dwc_otg_methods, 194230424Shselasky .size = sizeof(struct dwc_otg_super_softc), 195230424Shselasky}; 196230424Shselasky 197230424Shselaskystatic devclass_t dwc_otg_devclass; 198230424Shselasky 199230424ShselaskyDRIVER_MODULE(dwcotg, atmelarm, dwc_otg_driver, dwc_otg_devclass, 0, 0); 200230424ShselaskyMODULE_DEPEND(dwcotg, usb, 1, 1, 1); 201