1239281Sgonzo/*- 2239281Sgonzo * Copyright (c) 2011 3239281Sgonzo * Ben Gray <ben.r.gray@gmail.com>. 4239281Sgonzo * All rights reserved. 5239281Sgonzo * 6239281Sgonzo * Redistribution and use in source and binary forms, with or without 7239281Sgonzo * modification, are permitted provided that the following conditions 8239281Sgonzo * are met: 9239281Sgonzo * 1. Redistributions of source code must retain the above copyright 10239281Sgonzo * notice, this list of conditions and the following disclaimer. 11239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12239281Sgonzo * notice, this list of conditions and the following disclaimer in the 13239281Sgonzo * documentation and/or other materials provided with the distribution. 14239281Sgonzo * 15239281Sgonzo * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18239281Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25239281Sgonzo * SUCH DAMAGE. 26239281Sgonzo */ 27239281Sgonzo#include <sys/cdefs.h> 28239281Sgonzo__FBSDID("$FreeBSD: releng/11.0/sys/arm/ti/usb/omap_ehci.c 283276 2015-05-22 03:16:18Z gonzo $"); 29239281Sgonzo 30239281Sgonzo#include <sys/param.h> 31239281Sgonzo#include <sys/systm.h> 32283276Sgonzo#include <sys/conf.h> 33239281Sgonzo#include <sys/kernel.h> 34239281Sgonzo#include <sys/rman.h> 35239281Sgonzo#include <sys/module.h> 36283276Sgonzo#include <sys/proc.h> 37239281Sgonzo#include <sys/condvar.h> 38239281Sgonzo 39239281Sgonzo#include <dev/fdt/fdt_common.h> 40283276Sgonzo#include <dev/fdt/simplebus.h> 41239281Sgonzo#include <dev/ofw/ofw_bus_subr.h> 42239281Sgonzo 43239281Sgonzo#include <dev/usb/usb.h> 44239281Sgonzo#include <dev/usb/usbdi.h> 45239281Sgonzo 46239281Sgonzo#include <dev/usb/usb_core.h> 47239281Sgonzo#include <dev/usb/usb_busdma.h> 48239281Sgonzo#include <dev/usb/usb_process.h> 49239281Sgonzo#include <dev/usb/usb_util.h> 50239281Sgonzo 51239281Sgonzo#include <dev/usb/usb_controller.h> 52239281Sgonzo#include <dev/usb/usb_bus.h> 53239281Sgonzo#include <dev/usb/controller/ehci.h> 54239281Sgonzo#include <dev/usb/controller/ehcireg.h> 55239281Sgonzo 56283276Sgonzo#include <machine/bus.h> 57283276Sgonzo 58239281Sgonzo#include <arm/ti/ti_prcm.h> 59239281Sgonzo#include <arm/ti/usb/omap_usb.h> 60239281Sgonzo 61283276Sgonzo/* EHCI */ 62283276Sgonzo#define OMAP_USBHOST_HCCAPBASE 0x0000 63283276Sgonzo#define OMAP_USBHOST_HCSPARAMS 0x0004 64283276Sgonzo#define OMAP_USBHOST_HCCPARAMS 0x0008 65283276Sgonzo#define OMAP_USBHOST_USBCMD 0x0010 66283276Sgonzo#define OMAP_USBHOST_USBSTS 0x0014 67283276Sgonzo#define OMAP_USBHOST_USBINTR 0x0018 68283276Sgonzo#define OMAP_USBHOST_FRINDEX 0x001C 69283276Sgonzo#define OMAP_USBHOST_CTRLDSSEGMENT 0x0020 70283276Sgonzo#define OMAP_USBHOST_PERIODICLISTBASE 0x0024 71283276Sgonzo#define OMAP_USBHOST_ASYNCLISTADDR 0x0028 72283276Sgonzo#define OMAP_USBHOST_CONFIGFLAG 0x0050 73283276Sgonzo#define OMAP_USBHOST_PORTSC(i) (0x0054 + (0x04 * (i))) 74283276Sgonzo#define OMAP_USBHOST_INSNREG00 0x0090 75283276Sgonzo#define OMAP_USBHOST_INSNREG01 0x0094 76283276Sgonzo#define OMAP_USBHOST_INSNREG02 0x0098 77283276Sgonzo#define OMAP_USBHOST_INSNREG03 0x009C 78283276Sgonzo#define OMAP_USBHOST_INSNREG04 0x00A0 79283276Sgonzo#define OMAP_USBHOST_INSNREG05_UTMI 0x00A4 80283276Sgonzo#define OMAP_USBHOST_INSNREG05_ULPI 0x00A4 81283276Sgonzo#define OMAP_USBHOST_INSNREG06 0x00A8 82283276Sgonzo#define OMAP_USBHOST_INSNREG07 0x00AC 83283276Sgonzo#define OMAP_USBHOST_INSNREG08 0x00B0 84239281Sgonzo 85283276Sgonzo#define OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND (1 << 5) 86239281Sgonzo 87283276Sgonzo#define OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT 31 88283276Sgonzo#define OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT 24 89283276Sgonzo#define OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT 22 90283276Sgonzo#define OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT 16 91283276Sgonzo#define OMAP_USBHOST_INSNREG05_ULPI_EXTREGADD_SHIFT 8 92283276Sgonzo#define OMAP_USBHOST_INSNREG05_ULPI_WRDATA_SHIFT 0 93239281Sgonzo 94283276Sgonzo#define ULPI_FUNC_CTRL_RESET (1 << 5) 95239281Sgonzo 96283276Sgonzo/*-------------------------------------------------------------------------*/ 97239281Sgonzo 98283276Sgonzo/* 99283276Sgonzo * Macros for Set and Clear 100283276Sgonzo * See ULPI 1.1 specification to find the registers with Set and Clear offsets 101283276Sgonzo */ 102283276Sgonzo#define ULPI_SET(a) (a + 1) 103283276Sgonzo#define ULPI_CLR(a) (a + 2) 104239281Sgonzo 105283276Sgonzo/*-------------------------------------------------------------------------*/ 106239281Sgonzo 107283276Sgonzo/* 108283276Sgonzo * Register Map 109239281Sgonzo */ 110283276Sgonzo#define ULPI_VENDOR_ID_LOW 0x00 111283276Sgonzo#define ULPI_VENDOR_ID_HIGH 0x01 112283276Sgonzo#define ULPI_PRODUCT_ID_LOW 0x02 113283276Sgonzo#define ULPI_PRODUCT_ID_HIGH 0x03 114283276Sgonzo#define ULPI_FUNC_CTRL 0x04 115283276Sgonzo#define ULPI_IFC_CTRL 0x07 116283276Sgonzo#define ULPI_OTG_CTRL 0x0a 117283276Sgonzo#define ULPI_USB_INT_EN_RISE 0x0d 118283276Sgonzo#define ULPI_USB_INT_EN_FALL 0x10 119283276Sgonzo#define ULPI_USB_INT_STS 0x13 120283276Sgonzo#define ULPI_USB_INT_LATCH 0x14 121283276Sgonzo#define ULPI_DEBUG 0x15 122283276Sgonzo#define ULPI_SCRATCH 0x16 123239281Sgonzo 124283276Sgonzo#define OMAP_EHCI_HC_DEVSTR "TI OMAP USB 2.0 controller" 125239281Sgonzo 126283276Sgonzostruct omap_ehci_softc { 127283276Sgonzo ehci_softc_t base; /* storage for EHCI code */ 128283276Sgonzo device_t sc_dev; 129283276Sgonzo}; 130239281Sgonzo 131283276Sgonzostatic device_attach_t omap_ehci_attach; 132283276Sgonzostatic device_detach_t omap_ehci_detach; 133239281Sgonzo 134239281Sgonzo/** 135239281Sgonzo * omap_ehci_read_4 - read a 32-bit value from the EHCI registers 136239281Sgonzo * omap_ehci_write_4 - write a 32-bit value from the EHCI registers 137239281Sgonzo * @sc: omap ehci device context 138239281Sgonzo * @off: byte offset within the register set to read from 139239281Sgonzo * @val: the value to write into the register 140239281Sgonzo * 141239281Sgonzo * 142239281Sgonzo * LOCKING: 143239281Sgonzo * None 144239281Sgonzo * 145239281Sgonzo * RETURNS: 146239281Sgonzo * nothing in case of write function, if read function returns the value read. 147239281Sgonzo */ 148239281Sgonzostatic inline uint32_t 149239281Sgonzoomap_ehci_read_4(struct omap_ehci_softc *sc, bus_size_t off) 150239281Sgonzo{ 151239281Sgonzo return (bus_read_4(sc->base.sc_io_res, off)); 152239281Sgonzo} 153283276Sgonzo 154239281Sgonzostatic inline void 155239281Sgonzoomap_ehci_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) 156239281Sgonzo{ 157239281Sgonzo bus_write_4(sc->base.sc_io_res, off, val); 158239281Sgonzo} 159239281Sgonzo 160239281Sgonzo/** 161239281Sgonzo * omap_ehci_soft_phy_reset - resets the phy using the reset command 162239281Sgonzo * @isc: omap ehci device context 163239281Sgonzo * @port: port to send the reset over 164239281Sgonzo * 165239281Sgonzo * 166239281Sgonzo * LOCKING: 167239281Sgonzo * none 168239281Sgonzo * 169239281Sgonzo * RETURNS: 170239281Sgonzo * nothing 171239281Sgonzo */ 172239281Sgonzostatic void 173239281Sgonzoomap_ehci_soft_phy_reset(struct omap_ehci_softc *isc, unsigned int port) 174239281Sgonzo{ 175239281Sgonzo unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); 176239281Sgonzo uint32_t reg; 177239281Sgonzo 178239281Sgonzo reg = ULPI_FUNC_CTRL_RESET 179239281Sgonzo /* FUNCTION_CTRL_SET register */ 180239281Sgonzo | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT) 181239281Sgonzo /* Write */ 182239281Sgonzo | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT) 183239281Sgonzo /* PORTn */ 184239281Sgonzo | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT) 185239281Sgonzo /* start ULPI access*/ 186239281Sgonzo | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT); 187239281Sgonzo 188239281Sgonzo omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG05_ULPI, reg); 189239281Sgonzo 190239281Sgonzo /* Wait for ULPI access completion */ 191239281Sgonzo while ((omap_ehci_read_4(isc, OMAP_USBHOST_INSNREG05_ULPI) 192239281Sgonzo & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) { 193239281Sgonzo 194239281Sgonzo /* Sleep for a tick */ 195239281Sgonzo pause("USBPHY_RESET", 1); 196239281Sgonzo 197239281Sgonzo if (timeout-- == 0) { 198239281Sgonzo device_printf(isc->sc_dev, "PHY reset operation timed out\n"); 199239281Sgonzo break; 200239281Sgonzo } 201239281Sgonzo } 202239281Sgonzo} 203239281Sgonzo 204239281Sgonzo/** 205239281Sgonzo * omap_ehci_init - initialises the USB host EHCI controller 206239281Sgonzo * @isc: omap ehci device context 207239281Sgonzo * 208239281Sgonzo * This initialisation routine is quite heavily based on the work done by the 209239281Sgonzo * OMAP Linux team (for which I thank them very much). The init sequence is 210239281Sgonzo * almost identical, diverging only for the FreeBSD specifics. 211239281Sgonzo * 212239281Sgonzo * LOCKING: 213239281Sgonzo * none 214239281Sgonzo * 215239281Sgonzo * RETURNS: 216239281Sgonzo * 0 on success, a negative error code on failure. 217239281Sgonzo */ 218239281Sgonzostatic int 219239281Sgonzoomap_ehci_init(struct omap_ehci_softc *isc) 220239281Sgonzo{ 221239281Sgonzo uint32_t reg = 0; 222239281Sgonzo int i; 223283276Sgonzo device_t uhh_dev; 224239281Sgonzo 225283276Sgonzo uhh_dev = device_get_parent(isc->sc_dev); 226239281Sgonzo device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); 227239281Sgonzo 228239281Sgonzo /* Set the interrupt threshold control, it controls the maximum rate at 229239281Sgonzo * which the host controller issues interrupts. We set it to 1 microframe 230239281Sgonzo * at startup - the default is 8 mircoframes (equates to 1ms). 231239281Sgonzo */ 232239281Sgonzo reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD); 233239281Sgonzo reg &= 0xff00ffff; 234239281Sgonzo reg |= (1 << 16); 235239281Sgonzo omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg); 236239281Sgonzo 237239281Sgonzo /* Soft reset the PHY using PHY reset command over ULPI */ 238283276Sgonzo for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 239283276Sgonzo if (omap_usb_port_mode(uhh_dev, i) == EHCI_HCD_OMAP_MODE_PHY) 240283276Sgonzo omap_ehci_soft_phy_reset(isc, i); 241239281Sgonzo 242239281Sgonzo } 243239281Sgonzo 244283276Sgonzo return(0); 245239281Sgonzo} 246239281Sgonzo 247239281Sgonzo/** 248239281Sgonzo * omap_ehci_probe - starts the given command 249239281Sgonzo * @dev: 250239281Sgonzo * 251239281Sgonzo * Effectively boilerplate EHCI resume code. 252239281Sgonzo * 253239281Sgonzo * LOCKING: 254239281Sgonzo * Caller should be holding the OMAP3_MMC lock. 255239281Sgonzo * 256239281Sgonzo * RETURNS: 257239281Sgonzo * EH_HANDLED or EH_NOT_HANDLED 258239281Sgonzo */ 259239281Sgonzostatic int 260239281Sgonzoomap_ehci_probe(device_t dev) 261239281Sgonzo{ 262261410Sian 263261410Sian if (!ofw_bus_status_okay(dev)) 264261410Sian return (ENXIO); 265261410Sian 266283276Sgonzo if (!ofw_bus_is_compatible(dev, "ti,ehci-omap")) 267239281Sgonzo return (ENXIO); 268239281Sgonzo 269239281Sgonzo device_set_desc(dev, OMAP_EHCI_HC_DEVSTR); 270239281Sgonzo 271239281Sgonzo return (BUS_PROBE_DEFAULT); 272239281Sgonzo} 273239281Sgonzo 274239281Sgonzo/** 275239281Sgonzo * omap_ehci_attach - driver entry point, sets up the ECHI controller/driver 276239281Sgonzo * @dev: the new device handle 277239281Sgonzo * 278239281Sgonzo * Sets up bus spaces, interrupt handles, etc for the EHCI controller. It also 279239281Sgonzo * parses the resource hints and calls omap_ehci_init() to initialise the 280239281Sgonzo * H/W. 281239281Sgonzo * 282239281Sgonzo * LOCKING: 283239281Sgonzo * none 284239281Sgonzo * 285239281Sgonzo * RETURNS: 286239281Sgonzo * 0 on success or a positive error code on failure. 287239281Sgonzo */ 288239281Sgonzostatic int 289239281Sgonzoomap_ehci_attach(device_t dev) 290239281Sgonzo{ 291239281Sgonzo struct omap_ehci_softc *isc = device_get_softc(dev); 292239281Sgonzo ehci_softc_t *sc = &isc->base; 293239281Sgonzo int err; 294239281Sgonzo int rid; 295239281Sgonzo 296239281Sgonzo /* initialise some bus fields */ 297239281Sgonzo sc->sc_bus.parent = dev; 298239281Sgonzo sc->sc_bus.devices = sc->sc_devices; 299239281Sgonzo sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 300276717Shselasky sc->sc_bus.dma_bits = 32; 301276717Shselasky 302283276Sgonzo sprintf(sc->sc_vendor, "Texas Instruments"); 303283276Sgonzo 304239281Sgonzo /* save the device */ 305239281Sgonzo isc->sc_dev = dev; 306239281Sgonzo 307239281Sgonzo /* get all DMA memory */ 308239281Sgonzo if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), 309239281Sgonzo &ehci_iterate_hw_softc)) { 310239281Sgonzo return (ENOMEM); 311239281Sgonzo } 312239281Sgonzo 313239281Sgonzo /* Allocate resource for the EHCI register set */ 314239281Sgonzo rid = 0; 315239281Sgonzo sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 316239281Sgonzo if (!sc->sc_io_res) { 317239281Sgonzo device_printf(dev, "Error: Could not map EHCI memory\n"); 318239281Sgonzo goto error; 319239281Sgonzo } 320239281Sgonzo /* Request an interrupt resource */ 321239281Sgonzo rid = 0; 322239281Sgonzo sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 323239281Sgonzo if (sc->sc_irq_res == NULL) { 324239281Sgonzo device_printf(dev, "Error: could not allocate irq\n"); 325239281Sgonzo goto error; 326239281Sgonzo } 327239281Sgonzo 328239281Sgonzo /* Add this device as a child of the USBus device */ 329239281Sgonzo sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 330239281Sgonzo if (!sc->sc_bus.bdev) { 331239281Sgonzo device_printf(dev, "Error: could not add USB device\n"); 332239281Sgonzo goto error; 333239281Sgonzo } 334239281Sgonzo 335239281Sgonzo device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 336239281Sgonzo device_set_desc(sc->sc_bus.bdev, OMAP_EHCI_HC_DEVSTR); 337239281Sgonzo 338239281Sgonzo /* Initialise the ECHI registers */ 339239281Sgonzo err = omap_ehci_init(isc); 340239281Sgonzo if (err) { 341239281Sgonzo device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); 342239281Sgonzo goto error; 343239281Sgonzo } 344239281Sgonzo 345239281Sgonzo /* Set the tag and size of the register set in the EHCI context */ 346239281Sgonzo sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 347239281Sgonzo sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 348239281Sgonzo sc->sc_io_size = rman_get_size(sc->sc_io_res); 349239281Sgonzo 350239281Sgonzo /* Setup the interrupt */ 351239281Sgonzo err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 352239281Sgonzo NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 353239281Sgonzo if (err) { 354239281Sgonzo device_printf(dev, "Error: could not setup irq, %d\n", err); 355239281Sgonzo sc->sc_intr_hdl = NULL; 356239281Sgonzo goto error; 357239281Sgonzo } 358239281Sgonzo 359239281Sgonzo /* Finally we are ready to kick off the ECHI host controller */ 360239281Sgonzo err = ehci_init(sc); 361239281Sgonzo if (err == 0) { 362239281Sgonzo err = device_probe_and_attach(sc->sc_bus.bdev); 363239281Sgonzo } 364239281Sgonzo if (err) { 365239281Sgonzo device_printf(dev, "Error: USB init failed err=%d\n", err); 366239281Sgonzo goto error; 367239281Sgonzo } 368239281Sgonzo 369239281Sgonzo return (0); 370239281Sgonzo 371239281Sgonzoerror: 372239281Sgonzo omap_ehci_detach(dev); 373239281Sgonzo return (ENXIO); 374239281Sgonzo} 375239281Sgonzo 376239281Sgonzo/** 377239281Sgonzo * omap_ehci_detach - detach the device and cleanup the driver 378239281Sgonzo * @dev: device handle 379239281Sgonzo * 380239281Sgonzo * Clean-up routine where everything initialised in omap_ehci_attach is 381239281Sgonzo * freed and cleaned up. This function calls omap_ehci_fini() to shutdown 382239281Sgonzo * the on-chip module. 383239281Sgonzo * 384239281Sgonzo * LOCKING: 385239281Sgonzo * none 386239281Sgonzo * 387239281Sgonzo * RETURNS: 388239281Sgonzo * Always returns 0 (success). 389239281Sgonzo */ 390239281Sgonzostatic int 391239281Sgonzoomap_ehci_detach(device_t dev) 392239281Sgonzo{ 393239281Sgonzo struct omap_ehci_softc *isc = device_get_softc(dev); 394239281Sgonzo ehci_softc_t *sc = &isc->base; 395239281Sgonzo device_t bdev; 396239281Sgonzo int err; 397239281Sgonzo 398239281Sgonzo if (sc->sc_bus.bdev) { 399239281Sgonzo bdev = sc->sc_bus.bdev; 400239281Sgonzo device_detach(bdev); 401239281Sgonzo device_delete_child(dev, bdev); 402239281Sgonzo } 403239281Sgonzo 404239281Sgonzo /* during module unload there are lots of children leftover */ 405239281Sgonzo device_delete_children(dev); 406239281Sgonzo 407239281Sgonzo /* 408239281Sgonzo * disable interrupts that might have been switched on in ehci_init 409239281Sgonzo */ 410239281Sgonzo if (sc->sc_io_res) { 411239281Sgonzo EWRITE4(sc, EHCI_USBINTR, 0); 412239281Sgonzo } 413239281Sgonzo 414239281Sgonzo if (sc->sc_irq_res && sc->sc_intr_hdl) { 415239281Sgonzo /* 416239281Sgonzo * only call ehci_detach() after ehci_init() 417239281Sgonzo */ 418239281Sgonzo ehci_detach(sc); 419239281Sgonzo 420239281Sgonzo err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); 421239281Sgonzo if (err) 422239281Sgonzo device_printf(dev, "Error: could not tear down irq, %d\n", err); 423239281Sgonzo sc->sc_intr_hdl = NULL; 424239281Sgonzo } 425239281Sgonzo 426239281Sgonzo /* Free the resources stored in the base EHCI handler */ 427239281Sgonzo if (sc->sc_irq_res) { 428239281Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 429239281Sgonzo sc->sc_irq_res = NULL; 430239281Sgonzo } 431239281Sgonzo if (sc->sc_io_res) { 432239281Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); 433239281Sgonzo sc->sc_io_res = NULL; 434239281Sgonzo } 435239281Sgonzo 436239281Sgonzo return (0); 437239281Sgonzo} 438239281Sgonzo 439239281Sgonzostatic device_method_t ehci_methods[] = { 440239281Sgonzo /* Device interface */ 441239281Sgonzo DEVMETHOD(device_probe, omap_ehci_probe), 442239281Sgonzo DEVMETHOD(device_attach, omap_ehci_attach), 443239281Sgonzo DEVMETHOD(device_detach, omap_ehci_detach), 444283276Sgonzo 445283276Sgonzo DEVMETHOD(device_suspend, bus_generic_suspend), 446283276Sgonzo DEVMETHOD(device_resume, bus_generic_resume), 447283276Sgonzo DEVMETHOD(device_shutdown, bus_generic_shutdown), 448239281Sgonzo 449239281Sgonzo /* Bus interface */ 450239281Sgonzo DEVMETHOD(bus_print_child, bus_generic_print_child), 451239281Sgonzo 452239281Sgonzo {0, 0} 453239281Sgonzo}; 454239281Sgonzo 455239281Sgonzostatic driver_t ehci_driver = { 456239281Sgonzo "ehci", 457239281Sgonzo ehci_methods, 458239281Sgonzo sizeof(struct omap_ehci_softc), 459239281Sgonzo}; 460239281Sgonzo 461239281Sgonzostatic devclass_t ehci_devclass; 462239281Sgonzo 463283276SgonzoDRIVER_MODULE(ehci, omap_uhh, ehci_driver, ehci_devclass, 0, 0); 464