omap_ehci.c revision 239281
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 28239281Sgonzo/** 29239281Sgonzo * Driver for the High Speed USB EHCI module on the TI OMAP3530 SoC. 30239281Sgonzo * 31239281Sgonzo * WARNING: I've only tried this driver on a limited number of USB peripherals, 32239281Sgonzo * it is still very raw and bound to have numerous bugs in it. 33239281Sgonzo * 34239281Sgonzo * This driver is based on the FreeBSD IXP4xx EHCI driver with a lot of the 35239281Sgonzo * setup sequence coming from the Linux community and their EHCI driver for 36239281Sgonzo * OMAP. Without these as a base I don't think I would have been able to get 37239281Sgonzo * this driver working. 38239281Sgonzo * 39239281Sgonzo * The driver only contains the EHCI parts, the module also supports OHCI and 40239281Sgonzo * USB on-the-go (OTG), currently neither are supported. 41239281Sgonzo * 42239281Sgonzo * CAUTION: This driver was written to run on the beaglebaord dev board, so I 43239281Sgonzo * have made some assumptions about the type of PHY used and some of the other 44239281Sgonzo * settings. Bare that in mind if you intend to use this driver on another 45239281Sgonzo * platform. 46239281Sgonzo * 47239281Sgonzo * NOTE: This module uses a few different clocks, one being a 60Mhz clock for 48239281Sgonzo * the TTL part of the module. This clock is derived from DPPL5 which must be 49239281Sgonzo * configured prior to loading this driver - it is not configured by the 50239281Sgonzo * bootloader. It took me a long time to figure this out, and caused much 51239281Sgonzo * frustration. This PLL is now setup in the timer/clocks part of the BSP, 52239281Sgonzo * check out the omap_prcm_setup_dpll5() function in omap_prcm.c for more info. 53239281Sgonzo * 54239281Sgonzo */ 55239281Sgonzo 56239281Sgonzo#include <sys/cdefs.h> 57239281Sgonzo__FBSDID("$FreeBSD: head/sys/arm/ti/usb/omap_ehci.c 239281 2012-08-15 06:31:32Z gonzo $"); 58239281Sgonzo 59239281Sgonzo#include "opt_bus.h" 60239281Sgonzo 61239281Sgonzo#include <sys/stdint.h> 62239281Sgonzo#include <sys/stddef.h> 63239281Sgonzo#include <sys/param.h> 64239281Sgonzo#include <sys/queue.h> 65239281Sgonzo#include <sys/types.h> 66239281Sgonzo#include <sys/systm.h> 67239281Sgonzo#include <sys/kernel.h> 68239281Sgonzo#include <sys/bus.h> 69239281Sgonzo#include <sys/rman.h> 70239281Sgonzo#include <sys/linker_set.h> 71239281Sgonzo#include <sys/module.h> 72239281Sgonzo#include <sys/lock.h> 73239281Sgonzo#include <sys/mutex.h> 74239281Sgonzo#include <sys/condvar.h> 75239281Sgonzo#include <sys/sysctl.h> 76239281Sgonzo#include <sys/sx.h> 77239281Sgonzo#include <sys/unistd.h> 78239281Sgonzo#include <sys/callout.h> 79239281Sgonzo#include <sys/malloc.h> 80239281Sgonzo#include <sys/priv.h> 81239281Sgonzo#include <sys/gpio.h> 82239281Sgonzo 83239281Sgonzo#include <dev/fdt/fdt_common.h> 84239281Sgonzo#include <dev/ofw/openfirm.h> 85239281Sgonzo#include <dev/ofw/ofw_bus.h> 86239281Sgonzo#include <dev/ofw/ofw_bus_subr.h> 87239281Sgonzo 88239281Sgonzo#include <dev/usb/usb.h> 89239281Sgonzo#include <dev/usb/usbdi.h> 90239281Sgonzo 91239281Sgonzo#include <dev/usb/usb_core.h> 92239281Sgonzo#include <dev/usb/usb_busdma.h> 93239281Sgonzo#include <dev/usb/usb_process.h> 94239281Sgonzo#include <dev/usb/usb_util.h> 95239281Sgonzo 96239281Sgonzo#include <dev/usb/usb_controller.h> 97239281Sgonzo#include <dev/usb/usb_bus.h> 98239281Sgonzo#include <dev/usb/controller/ehci.h> 99239281Sgonzo#include <dev/usb/controller/ehcireg.h> 100239281Sgonzo 101239281Sgonzo#include <arm/ti/tivar.h> 102239281Sgonzo#include <arm/ti/ti_prcm.h> 103239281Sgonzo#include <arm/ti/ti_scm.h> 104239281Sgonzo 105239281Sgonzo#include <arm/ti/usb/omap_usb.h> 106239281Sgonzo 107239281Sgonzo#include "gpio_if.h" 108239281Sgonzo 109239281Sgonzostruct omap_ehci_softc { 110239281Sgonzo ehci_softc_t base; /* storage for EHCI code */ 111239281Sgonzo 112239281Sgonzo device_t sc_dev; 113239281Sgonzo device_t sc_gpio_dev; 114239281Sgonzo 115239281Sgonzo /* TLL register set */ 116239281Sgonzo struct resource* tll_mem_res; 117239281Sgonzo 118239281Sgonzo /* UHH register set */ 119239281Sgonzo struct resource* uhh_mem_res; 120239281Sgonzo 121239281Sgonzo /* The revision of the HS USB HOST read from UHH_REVISION */ 122239281Sgonzo uint32_t ehci_rev; 123239281Sgonzo 124239281Sgonzo /* The following details are provided by conf hints */ 125239281Sgonzo int port_mode[3]; 126239281Sgonzo int phy_reset[3]; 127239281Sgonzo int reset_gpio_pin[3]; 128239281Sgonzo}; 129239281Sgonzo 130239281Sgonzostatic device_attach_t omap_ehci_attach; 131239281Sgonzostatic device_detach_t omap_ehci_detach; 132239281Sgonzostatic device_shutdown_t omap_ehci_shutdown; 133239281Sgonzostatic device_suspend_t omap_ehci_suspend; 134239281Sgonzostatic device_resume_t omap_ehci_resume; 135239281Sgonzo 136239281Sgonzo/** 137239281Sgonzo * omap_tll_read_4 - read a 32-bit value from the USBTLL registers 138239281Sgonzo * omap_tll_write_4 - write a 32-bit value from the USBTLL registers 139239281Sgonzo * omap_tll_readb - read an 8-bit value from the USBTLL registers 140239281Sgonzo * omap_tll_writeb - write an 8-bit value from the USBTLL registers 141239281Sgonzo * @sc: omap ehci device context 142239281Sgonzo * @off: byte offset within the register set to read from 143239281Sgonzo * @val: the value to write into the register 144239281Sgonzo * 145239281Sgonzo * 146239281Sgonzo * LOCKING: 147239281Sgonzo * None 148239281Sgonzo * 149239281Sgonzo * RETURNS: 150239281Sgonzo * nothing in case of write function, if read function returns the value read. 151239281Sgonzo */ 152239281Sgonzostatic inline uint32_t 153239281Sgonzoomap_tll_read_4(struct omap_ehci_softc *sc, bus_size_t off) 154239281Sgonzo{ 155239281Sgonzo return bus_read_4(sc->tll_mem_res, off); 156239281Sgonzo} 157239281Sgonzo 158239281Sgonzostatic inline void 159239281Sgonzoomap_tll_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) 160239281Sgonzo{ 161239281Sgonzo bus_write_4(sc->tll_mem_res, off, val); 162239281Sgonzo} 163239281Sgonzo 164239281Sgonzostatic inline uint8_t 165239281Sgonzoomap_tll_readb(struct omap_ehci_softc *sc, bus_size_t off) 166239281Sgonzo{ 167239281Sgonzo return bus_read_1(sc->tll_mem_res, off); 168239281Sgonzo} 169239281Sgonzo 170239281Sgonzostatic inline void 171239281Sgonzoomap_tll_writeb(struct omap_ehci_softc *sc, bus_size_t off, uint8_t val) 172239281Sgonzo{ 173239281Sgonzo bus_write_1(sc->tll_mem_res, off, val); 174239281Sgonzo} 175239281Sgonzo 176239281Sgonzo/** 177239281Sgonzo * omap_ehci_read_4 - read a 32-bit value from the EHCI registers 178239281Sgonzo * omap_ehci_write_4 - write a 32-bit value from the EHCI registers 179239281Sgonzo * @sc: omap ehci device context 180239281Sgonzo * @off: byte offset within the register set to read from 181239281Sgonzo * @val: the value to write into the register 182239281Sgonzo * 183239281Sgonzo * 184239281Sgonzo * LOCKING: 185239281Sgonzo * None 186239281Sgonzo * 187239281Sgonzo * RETURNS: 188239281Sgonzo * nothing in case of write function, if read function returns the value read. 189239281Sgonzo */ 190239281Sgonzostatic inline uint32_t 191239281Sgonzoomap_ehci_read_4(struct omap_ehci_softc *sc, bus_size_t off) 192239281Sgonzo{ 193239281Sgonzo return (bus_read_4(sc->base.sc_io_res, off)); 194239281Sgonzo} 195239281Sgonzostatic inline void 196239281Sgonzoomap_ehci_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) 197239281Sgonzo{ 198239281Sgonzo bus_write_4(sc->base.sc_io_res, off, val); 199239281Sgonzo} 200239281Sgonzo 201239281Sgonzo/** 202239281Sgonzo * omap_uhh_read_4 - read a 32-bit value from the UHH registers 203239281Sgonzo * omap_uhh_write_4 - write a 32-bit value from the UHH registers 204239281Sgonzo * @sc: omap ehci device context 205239281Sgonzo * @off: byte offset within the register set to read from 206239281Sgonzo * @val: the value to write into the register 207239281Sgonzo * 208239281Sgonzo * 209239281Sgonzo * LOCKING: 210239281Sgonzo * None 211239281Sgonzo * 212239281Sgonzo * RETURNS: 213239281Sgonzo * nothing in case of write function, if read function returns the value read. 214239281Sgonzo */ 215239281Sgonzostatic inline uint32_t 216239281Sgonzoomap_uhh_read_4(struct omap_ehci_softc *sc, bus_size_t off) 217239281Sgonzo{ 218239281Sgonzo return bus_read_4(sc->uhh_mem_res, off); 219239281Sgonzo} 220239281Sgonzostatic inline void 221239281Sgonzoomap_uhh_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) 222239281Sgonzo{ 223239281Sgonzo bus_write_4(sc->uhh_mem_res, off, val); 224239281Sgonzo} 225239281Sgonzo 226239281Sgonzo/** 227239281Sgonzo * omap_ehci_utmi_init - initialises the UTMI part of the controller 228239281Sgonzo * @isc: omap ehci device context 229239281Sgonzo * 230239281Sgonzo * 231239281Sgonzo * 232239281Sgonzo * LOCKING: 233239281Sgonzo * none 234239281Sgonzo * 235239281Sgonzo * RETURNS: 236239281Sgonzo * nothing 237239281Sgonzo */ 238239281Sgonzostatic void 239239281Sgonzoomap_ehci_utmi_init(struct omap_ehci_softc *isc, unsigned int en_mask) 240239281Sgonzo{ 241239281Sgonzo unsigned int i; 242239281Sgonzo uint32_t reg; 243239281Sgonzo 244239281Sgonzo /* There are 3 TLL channels, one per USB controller so set them all up the 245239281Sgonzo * same, SDR mode, bit stuffing and no autoidle. 246239281Sgonzo */ 247239281Sgonzo for (i=0; i<3; i++) { 248239281Sgonzo reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); 249239281Sgonzo 250239281Sgonzo reg &= ~(TLL_CHANNEL_CONF_UTMIAUTOIDLE 251239281Sgonzo | TLL_CHANNEL_CONF_ULPINOBITSTUFF 252239281Sgonzo | TLL_CHANNEL_CONF_ULPIDDRMODE); 253239281Sgonzo 254239281Sgonzo omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); 255239281Sgonzo } 256239281Sgonzo 257239281Sgonzo /* Program the common TLL register */ 258239281Sgonzo reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_SHARED_CONF); 259239281Sgonzo 260239281Sgonzo reg &= ~( TLL_SHARED_CONF_USB_90D_DDR_EN 261239281Sgonzo | TLL_SHARED_CONF_USB_DIVRATIO_MASK); 262239281Sgonzo reg |= ( TLL_SHARED_CONF_FCLK_IS_ON 263239281Sgonzo | TLL_SHARED_CONF_USB_DIVRATIO_2 264239281Sgonzo | TLL_SHARED_CONF_USB_180D_SDR_EN); 265239281Sgonzo 266239281Sgonzo omap_tll_write_4(isc, OMAP_USBTLL_TLL_SHARED_CONF, reg); 267239281Sgonzo 268239281Sgonzo /* Enable channels now */ 269239281Sgonzo for (i = 0; i < 3; i++) { 270239281Sgonzo reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); 271239281Sgonzo 272239281Sgonzo /* Enable only the reg that is needed */ 273239281Sgonzo if ((en_mask & (1 << i)) == 0) 274239281Sgonzo continue; 275239281Sgonzo 276239281Sgonzo reg |= TLL_CHANNEL_CONF_CHANEN; 277239281Sgonzo omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); 278239281Sgonzo } 279239281Sgonzo} 280239281Sgonzo 281239281Sgonzo/** 282239281Sgonzo * omap_ehci_soft_phy_reset - resets the phy using the reset command 283239281Sgonzo * @isc: omap ehci device context 284239281Sgonzo * @port: port to send the reset over 285239281Sgonzo * 286239281Sgonzo * 287239281Sgonzo * LOCKING: 288239281Sgonzo * none 289239281Sgonzo * 290239281Sgonzo * RETURNS: 291239281Sgonzo * nothing 292239281Sgonzo */ 293239281Sgonzostatic void 294239281Sgonzoomap_ehci_soft_phy_reset(struct omap_ehci_softc *isc, unsigned int port) 295239281Sgonzo{ 296239281Sgonzo unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); 297239281Sgonzo uint32_t reg; 298239281Sgonzo 299239281Sgonzo reg = ULPI_FUNC_CTRL_RESET 300239281Sgonzo /* FUNCTION_CTRL_SET register */ 301239281Sgonzo | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT) 302239281Sgonzo /* Write */ 303239281Sgonzo | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT) 304239281Sgonzo /* PORTn */ 305239281Sgonzo | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT) 306239281Sgonzo /* start ULPI access*/ 307239281Sgonzo | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT); 308239281Sgonzo 309239281Sgonzo omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG05_ULPI, reg); 310239281Sgonzo 311239281Sgonzo /* Wait for ULPI access completion */ 312239281Sgonzo while ((omap_ehci_read_4(isc, OMAP_USBHOST_INSNREG05_ULPI) 313239281Sgonzo & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) { 314239281Sgonzo 315239281Sgonzo /* Sleep for a tick */ 316239281Sgonzo pause("USBPHY_RESET", 1); 317239281Sgonzo 318239281Sgonzo if (timeout-- == 0) { 319239281Sgonzo device_printf(isc->sc_dev, "PHY reset operation timed out\n"); 320239281Sgonzo break; 321239281Sgonzo } 322239281Sgonzo } 323239281Sgonzo} 324239281Sgonzo 325239281Sgonzo 326239281Sgonzo/** 327239281Sgonzo * omap_ehci_init - initialises the USB host EHCI controller 328239281Sgonzo * @isc: omap ehci device context 329239281Sgonzo * 330239281Sgonzo * This initialisation routine is quite heavily based on the work done by the 331239281Sgonzo * OMAP Linux team (for which I thank them very much). The init sequence is 332239281Sgonzo * almost identical, diverging only for the FreeBSD specifics. 333239281Sgonzo * 334239281Sgonzo * LOCKING: 335239281Sgonzo * none 336239281Sgonzo * 337239281Sgonzo * RETURNS: 338239281Sgonzo * 0 on success, a negative error code on failure. 339239281Sgonzo */ 340239281Sgonzostatic int 341239281Sgonzoomap_ehci_init(struct omap_ehci_softc *isc) 342239281Sgonzo{ 343239281Sgonzo unsigned long timeout; 344239281Sgonzo int ret = 0; 345239281Sgonzo uint8_t tll_ch_mask = 0; 346239281Sgonzo uint32_t reg = 0; 347239281Sgonzo int reset_performed = 0; 348239281Sgonzo int i; 349239281Sgonzo 350239281Sgonzo device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); 351239281Sgonzo 352239281Sgonzo 353239281Sgonzo /* Enable Clocks for high speed USBHOST */ 354239281Sgonzo ti_prcm_clk_enable(USBHSHOST_CLK); 355239281Sgonzo 356239281Sgonzo /* Hold the PHY in reset while configuring */ 357239281Sgonzo for (int i = 0; i < 3; i++) { 358239281Sgonzo if (isc->phy_reset[i]) { 359239281Sgonzo /* Configure the GPIO to drive low (hold in reset) */ 360239281Sgonzo if ((isc->reset_gpio_pin[i] != -1) && (isc->sc_gpio_dev != NULL)) { 361239281Sgonzo GPIO_PIN_SETFLAGS(isc->sc_gpio_dev, isc->reset_gpio_pin[i], 362239281Sgonzo GPIO_PIN_OUTPUT); 363239281Sgonzo GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[i], 364239281Sgonzo GPIO_PIN_LOW); 365239281Sgonzo reset_performed = 1; 366239281Sgonzo } 367239281Sgonzo } 368239281Sgonzo } 369239281Sgonzo 370239281Sgonzo /* Hold the PHY in RESET for enough time till DIR is high */ 371239281Sgonzo if (reset_performed) 372239281Sgonzo DELAY(10); 373239281Sgonzo 374239281Sgonzo /* Read the UHH revision */ 375239281Sgonzo isc->ehci_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION); 376239281Sgonzo device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->ehci_rev); 377239281Sgonzo 378239281Sgonzo /* Initilise the low level interface module(s) */ 379239281Sgonzo if (isc->ehci_rev == OMAP_EHCI_REV1) { 380239281Sgonzo 381239281Sgonzo /* Enable the USB TLL */ 382239281Sgonzo ti_prcm_clk_enable(USBTLL_CLK); 383239281Sgonzo 384239281Sgonzo /* Perform TLL soft reset, and wait until reset is complete */ 385239281Sgonzo omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET); 386239281Sgonzo 387239281Sgonzo /* Set the timeout to 100ms*/ 388239281Sgonzo timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); 389239281Sgonzo 390239281Sgonzo /* Wait for TLL reset to complete */ 391239281Sgonzo while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & 392239281Sgonzo TLL_SYSSTATUS_RESETDONE) == 0x00) { 393239281Sgonzo 394239281Sgonzo /* Sleep for a tick */ 395239281Sgonzo pause("USBRESET", 1); 396239281Sgonzo 397239281Sgonzo if (timeout-- == 0) { 398239281Sgonzo device_printf(isc->sc_dev, "TLL reset operation timed out\n"); 399239281Sgonzo ret = EINVAL; 400239281Sgonzo goto err_sys_status; 401239281Sgonzo } 402239281Sgonzo } 403239281Sgonzo 404239281Sgonzo device_printf(isc->sc_dev, "TLL RESET DONE\n"); 405239281Sgonzo 406239281Sgonzo /* CLOCKACTIVITY = 1 : OCP-derived internal clocks ON during idle 407239281Sgonzo * SIDLEMODE = 2 : Smart-idle mode. Sidleack asserted after Idlereq 408239281Sgonzo * assertion when no more activity on the USB. 409239281Sgonzo * ENAWAKEUP = 1 : Wakeup generation enabled 410239281Sgonzo */ 411239281Sgonzo omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP | 412239281Sgonzo TLL_SYSCONFIG_AUTOIDLE | 413239281Sgonzo TLL_SYSCONFIG_SIDLE_SMART_IDLE | 414239281Sgonzo TLL_SYSCONFIG_CACTIVITY); 415239281Sgonzo 416239281Sgonzo } else if (isc->ehci_rev == OMAP_EHCI_REV2) { 417239281Sgonzo 418239281Sgonzo /* For OMAP44xx devices you have to enable the per-port clocks: 419239281Sgonzo * PHY_MODE - External ULPI clock 420239281Sgonzo * TTL_MODE - Internal UTMI clock 421239281Sgonzo * HSIC_MODE - Internal 480Mhz and 60Mhz clocks 422239281Sgonzo */ 423239281Sgonzo if (isc->ehci_rev == OMAP_EHCI_REV2) { 424239281Sgonzo if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) { 425239281Sgonzo ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK); 426239281Sgonzo ti_prcm_clk_enable(USBP1_PHY_CLK); 427239281Sgonzo } else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) 428239281Sgonzo ti_prcm_clk_enable(USBP1_UTMI_CLK); 429239281Sgonzo else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) 430239281Sgonzo ti_prcm_clk_enable(USBP1_HSIC_CLK); 431239281Sgonzo 432239281Sgonzo if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) { 433239281Sgonzo ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK); 434239281Sgonzo ti_prcm_clk_enable(USBP2_PHY_CLK); 435239281Sgonzo } else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) 436239281Sgonzo ti_prcm_clk_enable(USBP2_UTMI_CLK); 437239281Sgonzo else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) 438239281Sgonzo ti_prcm_clk_enable(USBP2_HSIC_CLK); 439239281Sgonzo } 440239281Sgonzo } 441239281Sgonzo 442239281Sgonzo /* Put UHH in SmartIdle/SmartStandby mode */ 443239281Sgonzo reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG); 444239281Sgonzo if (isc->ehci_rev == OMAP_EHCI_REV1) { 445239281Sgonzo reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | 446239281Sgonzo UHH_SYSCONFIG_MIDLEMODE_MASK); 447239281Sgonzo reg |= (UHH_SYSCONFIG_ENAWAKEUP | 448239281Sgonzo UHH_SYSCONFIG_AUTOIDLE | 449239281Sgonzo UHH_SYSCONFIG_CLOCKACTIVITY | 450239281Sgonzo UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | 451239281Sgonzo UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); 452239281Sgonzo } else if (isc->ehci_rev == OMAP_EHCI_REV2) { 453239281Sgonzo reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; 454239281Sgonzo reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; 455239281Sgonzo reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; 456239281Sgonzo reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; 457239281Sgonzo } 458239281Sgonzo omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); 459239281Sgonzo device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); 460239281Sgonzo 461239281Sgonzo reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG); 462239281Sgonzo 463239281Sgonzo /* Setup ULPI bypass and burst configurations */ 464239281Sgonzo reg |= (UHH_HOSTCONFIG_ENA_INCR4 | 465239281Sgonzo UHH_HOSTCONFIG_ENA_INCR8 | 466239281Sgonzo UHH_HOSTCONFIG_ENA_INCR16); 467239281Sgonzo reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; 468239281Sgonzo 469239281Sgonzo if (isc->ehci_rev == OMAP_EHCI_REV1) { 470239281Sgonzo if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) 471239281Sgonzo reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; 472239281Sgonzo if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) 473239281Sgonzo reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; 474239281Sgonzo if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) 475239281Sgonzo reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; 476239281Sgonzo 477239281Sgonzo /* Bypass the TLL module for PHY mode operation */ 478239281Sgonzo if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || 479239281Sgonzo (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || 480239281Sgonzo (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) 481239281Sgonzo reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; 482239281Sgonzo else 483239281Sgonzo reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; 484239281Sgonzo 485239281Sgonzo } else if (isc->ehci_rev == OMAP_EHCI_REV2) { 486239281Sgonzo reg |= UHH_HOSTCONFIG_APP_START_CLK; 487239281Sgonzo 488239281Sgonzo /* Clear port mode fields for PHY mode*/ 489239281Sgonzo reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; 490239281Sgonzo reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; 491239281Sgonzo 492239281Sgonzo if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) 493239281Sgonzo reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; 494239281Sgonzo else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) 495239281Sgonzo reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; 496239281Sgonzo 497239281Sgonzo if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) 498239281Sgonzo reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; 499239281Sgonzo else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) 500239281Sgonzo reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; 501239281Sgonzo } 502239281Sgonzo 503239281Sgonzo omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg); 504239281Sgonzo device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg); 505239281Sgonzo 506239281Sgonzo 507239281Sgonzo /* I found the code and comments in the Linux EHCI driver - thanks guys :) 508239281Sgonzo * 509239281Sgonzo * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended 510239281Sgonzo * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared 511239281Sgonzo * (for example when we do ehci_bus_suspend). This breaks suspend-resume if 512239281Sgonzo * the root-hub is allowed to suspend. Writing 1 to this undocumented 513239281Sgonzo * register bit disables this feature and restores normal behavior." 514239281Sgonzo */ 515239281Sgonzo#if 0 516239281Sgonzo omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG04, 517239281Sgonzo OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); 518239281Sgonzo#endif 519239281Sgonzo 520239281Sgonzo /* If any of the ports are configured in TLL mode, enable them */ 521239281Sgonzo if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || 522239281Sgonzo (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || 523239281Sgonzo (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { 524239281Sgonzo 525239281Sgonzo if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) 526239281Sgonzo tll_ch_mask |= 0x1; 527239281Sgonzo if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) 528239281Sgonzo tll_ch_mask |= 0x2; 529239281Sgonzo if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) 530239281Sgonzo tll_ch_mask |= 0x4; 531239281Sgonzo 532239281Sgonzo /* Enable UTMI mode for required TLL channels */ 533239281Sgonzo omap_ehci_utmi_init(isc, tll_ch_mask); 534239281Sgonzo } 535239281Sgonzo 536239281Sgonzo 537239281Sgonzo /* Release the PHY reset signal now we have configured everything */ 538239281Sgonzo if (reset_performed) { 539239281Sgonzo 540239281Sgonzo /* Delay for 10ms */ 541239281Sgonzo DELAY(10000); 542239281Sgonzo 543239281Sgonzo for (i = 0; i < 3; i++) { 544239281Sgonzo /* Release reset */ 545239281Sgonzo 546239281Sgonzo if (isc->phy_reset[i] && (isc->reset_gpio_pin[i] != -1) 547239281Sgonzo && (isc->sc_gpio_dev != NULL)) { 548239281Sgonzo GPIO_PIN_SET(isc->sc_gpio_dev, 549239281Sgonzo isc->reset_gpio_pin[i], GPIO_PIN_HIGH); 550239281Sgonzo } 551239281Sgonzo } 552239281Sgonzo } 553239281Sgonzo 554239281Sgonzo /* Set the interrupt threshold control, it controls the maximum rate at 555239281Sgonzo * which the host controller issues interrupts. We set it to 1 microframe 556239281Sgonzo * at startup - the default is 8 mircoframes (equates to 1ms). 557239281Sgonzo */ 558239281Sgonzo reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD); 559239281Sgonzo reg &= 0xff00ffff; 560239281Sgonzo reg |= (1 << 16); 561239281Sgonzo omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg); 562239281Sgonzo 563239281Sgonzo /* Soft reset the PHY using PHY reset command over ULPI */ 564239281Sgonzo if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) 565239281Sgonzo omap_ehci_soft_phy_reset(isc, 0); 566239281Sgonzo if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) 567239281Sgonzo omap_ehci_soft_phy_reset(isc, 1); 568239281Sgonzo 569239281Sgonzo return(0); 570239281Sgonzo 571239281Sgonzoerr_sys_status: 572239281Sgonzo 573239281Sgonzo /* Disable the TLL clocks */ 574239281Sgonzo ti_prcm_clk_disable(USBTLL_CLK); 575239281Sgonzo 576239281Sgonzo /* Disable Clocks for USBHOST */ 577239281Sgonzo ti_prcm_clk_disable(USBHSHOST_CLK); 578239281Sgonzo 579239281Sgonzo return(ret); 580239281Sgonzo} 581239281Sgonzo 582239281Sgonzo 583239281Sgonzo/** 584239281Sgonzo * omap_ehci_fini - shutdown the EHCI controller 585239281Sgonzo * @isc: omap ehci device context 586239281Sgonzo * 587239281Sgonzo * 588239281Sgonzo * 589239281Sgonzo * LOCKING: 590239281Sgonzo * none 591239281Sgonzo * 592239281Sgonzo * RETURNS: 593239281Sgonzo * 0 on success, a negative error code on failure. 594239281Sgonzo */ 595239281Sgonzostatic void 596239281Sgonzoomap_ehci_fini(struct omap_ehci_softc *isc) 597239281Sgonzo{ 598239281Sgonzo unsigned long timeout; 599239281Sgonzo 600239281Sgonzo device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n"); 601239281Sgonzo 602239281Sgonzo /* Set the timeout */ 603239281Sgonzo if (hz < 10) 604239281Sgonzo timeout = 1; 605239281Sgonzo else 606239281Sgonzo timeout = (100 * hz) / 1000; 607239281Sgonzo 608239281Sgonzo /* Reset the UHH, OHCI and EHCI modules */ 609239281Sgonzo omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); 610239281Sgonzo while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) { 611239281Sgonzo /* Sleep for a tick */ 612239281Sgonzo pause("USBRESET", 1); 613239281Sgonzo 614239281Sgonzo if (timeout-- == 0) { 615239281Sgonzo device_printf(isc->sc_dev, "operation timed out\n"); 616239281Sgonzo break; 617239281Sgonzo } 618239281Sgonzo } 619239281Sgonzo 620239281Sgonzo 621239281Sgonzo /* Set the timeout */ 622239281Sgonzo if (hz < 10) 623239281Sgonzo timeout = 1; 624239281Sgonzo else 625239281Sgonzo timeout = (100 * hz) / 1000; 626239281Sgonzo 627239281Sgonzo /* Reset the TLL module */ 628239281Sgonzo omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, 0x0002); 629239281Sgonzo while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & (0x01)) == 0x00) { 630239281Sgonzo /* Sleep for a tick */ 631239281Sgonzo pause("USBRESET", 1); 632239281Sgonzo 633239281Sgonzo if (timeout-- == 0) { 634239281Sgonzo device_printf(isc->sc_dev, "operation timed out\n"); 635239281Sgonzo break; 636239281Sgonzo } 637239281Sgonzo } 638239281Sgonzo 639239281Sgonzo 640239281Sgonzo /* Disable functional and interface clocks for the TLL and HOST modules */ 641239281Sgonzo ti_prcm_clk_disable(USBTLL_CLK); 642239281Sgonzo ti_prcm_clk_disable(USBHSHOST_CLK); 643239281Sgonzo 644239281Sgonzo device_printf(isc->sc_dev, "Clock to USB host has been disabled\n"); 645239281Sgonzo 646239281Sgonzo} 647239281Sgonzo 648239281Sgonzo 649239281Sgonzo 650239281Sgonzo/** 651239281Sgonzo * omap_ehci_suspend - suspends the bus 652239281Sgonzo * @dev: omap ehci device 653239281Sgonzo * 654239281Sgonzo * Effectively boilerplate EHCI suspend code. 655239281Sgonzo * 656239281Sgonzo * TODO: There is a lot more we could do here - i.e. force the controller into 657239281Sgonzo * idle mode and disable all the clocks for start. 658239281Sgonzo * 659239281Sgonzo * LOCKING: 660239281Sgonzo * none 661239281Sgonzo * 662239281Sgonzo * RETURNS: 663239281Sgonzo * 0 on success or a positive error code 664239281Sgonzo */ 665239281Sgonzostatic int 666239281Sgonzoomap_ehci_suspend(device_t dev) 667239281Sgonzo{ 668239281Sgonzo ehci_softc_t *sc = device_get_softc(dev); 669239281Sgonzo int err; 670239281Sgonzo 671239281Sgonzo sc = sc; 672239281Sgonzo err = bus_generic_suspend(dev); 673239281Sgonzo if (err) 674239281Sgonzo return (err); 675239281Sgonzo return (0); 676239281Sgonzo} 677239281Sgonzo 678239281Sgonzo 679239281Sgonzo/** 680239281Sgonzo * omap_ehci_resume - resumes a suspended bus 681239281Sgonzo * @dev: omap ehci device 682239281Sgonzo * 683239281Sgonzo * Effectively boilerplate EHCI resume code. 684239281Sgonzo * 685239281Sgonzo * LOCKING: 686239281Sgonzo * none 687239281Sgonzo * 688239281Sgonzo * RETURNS: 689239281Sgonzo * 0 on success or a positive error code on failure 690239281Sgonzo */ 691239281Sgonzostatic int 692239281Sgonzoomap_ehci_resume(device_t dev) 693239281Sgonzo{ 694239281Sgonzo ehci_softc_t *sc = device_get_softc(dev); 695239281Sgonzo sc = sc; 696239281Sgonzo 697239281Sgonzo bus_generic_resume(dev); 698239281Sgonzo 699239281Sgonzo return (0); 700239281Sgonzo} 701239281Sgonzo 702239281Sgonzo 703239281Sgonzo/** 704239281Sgonzo * omap_ehci_shutdown - starts the given command 705239281Sgonzo * @dev: 706239281Sgonzo * 707239281Sgonzo * Effectively boilerplate EHCI shutdown code. 708239281Sgonzo * 709239281Sgonzo * LOCKING: 710239281Sgonzo * none. 711239281Sgonzo * 712239281Sgonzo * RETURNS: 713239281Sgonzo * 0 on success or a positive error code on failure 714239281Sgonzo */ 715239281Sgonzostatic int 716239281Sgonzoomap_ehci_shutdown(device_t dev) 717239281Sgonzo{ 718239281Sgonzo ehci_softc_t *sc = device_get_softc(dev); 719239281Sgonzo int err; 720239281Sgonzo 721239281Sgonzo sc = sc; 722239281Sgonzo err = bus_generic_shutdown(dev); 723239281Sgonzo if (err) 724239281Sgonzo return (err); 725239281Sgonzo 726239281Sgonzo return (0); 727239281Sgonzo} 728239281Sgonzo 729239281Sgonzo 730239281Sgonzo/** 731239281Sgonzo * omap_ehci_probe - starts the given command 732239281Sgonzo * @dev: 733239281Sgonzo * 734239281Sgonzo * Effectively boilerplate EHCI resume code. 735239281Sgonzo * 736239281Sgonzo * LOCKING: 737239281Sgonzo * Caller should be holding the OMAP3_MMC lock. 738239281Sgonzo * 739239281Sgonzo * RETURNS: 740239281Sgonzo * EH_HANDLED or EH_NOT_HANDLED 741239281Sgonzo */ 742239281Sgonzostatic int 743239281Sgonzoomap_ehci_probe(device_t dev) 744239281Sgonzo{ 745239281Sgonzo if (!ofw_bus_is_compatible(dev, "ti,usb-ehci")) 746239281Sgonzo return (ENXIO); 747239281Sgonzo 748239281Sgonzo device_set_desc(dev, OMAP_EHCI_HC_DEVSTR); 749239281Sgonzo 750239281Sgonzo return (BUS_PROBE_DEFAULT); 751239281Sgonzo} 752239281Sgonzo 753239281Sgonzo/** 754239281Sgonzo * omap_ehci_attach - driver entry point, sets up the ECHI controller/driver 755239281Sgonzo * @dev: the new device handle 756239281Sgonzo * 757239281Sgonzo * Sets up bus spaces, interrupt handles, etc for the EHCI controller. It also 758239281Sgonzo * parses the resource hints and calls omap_ehci_init() to initialise the 759239281Sgonzo * H/W. 760239281Sgonzo * 761239281Sgonzo * LOCKING: 762239281Sgonzo * none 763239281Sgonzo * 764239281Sgonzo * RETURNS: 765239281Sgonzo * 0 on success or a positive error code on failure. 766239281Sgonzo */ 767239281Sgonzostatic int 768239281Sgonzoomap_ehci_attach(device_t dev) 769239281Sgonzo{ 770239281Sgonzo struct omap_ehci_softc *isc = device_get_softc(dev); 771239281Sgonzo phandle_t node; 772239281Sgonzo /* 3 ports with 3 cells per port */ 773239281Sgonzo pcell_t phyconf[3 * 3]; 774239281Sgonzo pcell_t *phyconf_ptr; 775239281Sgonzo ehci_softc_t *sc = &isc->base; 776239281Sgonzo int err; 777239281Sgonzo int rid; 778239281Sgonzo int len, tuple_size; 779239281Sgonzo int i; 780239281Sgonzo 781239281Sgonzo /* initialise some bus fields */ 782239281Sgonzo sc->sc_bus.parent = dev; 783239281Sgonzo sc->sc_bus.devices = sc->sc_devices; 784239281Sgonzo sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 785239281Sgonzo 786239281Sgonzo /* save the device */ 787239281Sgonzo isc->sc_dev = dev; 788239281Sgonzo 789239281Sgonzo /* get all DMA memory */ 790239281Sgonzo if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), 791239281Sgonzo &ehci_iterate_hw_softc)) { 792239281Sgonzo return (ENOMEM); 793239281Sgonzo } 794239281Sgonzo 795239281Sgonzo /* When the EHCI driver is added to the tree it is expected that 3 796239281Sgonzo * memory resources and 1 interrupt resource is assigned. The memory 797239281Sgonzo * resources should be: 798239281Sgonzo * 0 => EHCI register range 799239281Sgonzo * 1 => UHH register range 800239281Sgonzo * 2 => TLL register range 801239281Sgonzo * 802239281Sgonzo * The interrupt resource is just the single interupt for the controller. 803239281Sgonzo */ 804239281Sgonzo 805239281Sgonzo /* Allocate resource for the EHCI register set */ 806239281Sgonzo rid = 0; 807239281Sgonzo sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 808239281Sgonzo if (!sc->sc_io_res) { 809239281Sgonzo device_printf(dev, "Error: Could not map EHCI memory\n"); 810239281Sgonzo goto error; 811239281Sgonzo } 812239281Sgonzo /* Request an interrupt resource */ 813239281Sgonzo rid = 0; 814239281Sgonzo sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 815239281Sgonzo if (sc->sc_irq_res == NULL) { 816239281Sgonzo device_printf(dev, "Error: could not allocate irq\n"); 817239281Sgonzo goto error; 818239281Sgonzo } 819239281Sgonzo 820239281Sgonzo /* Allocate resource for the UHH register set */ 821239281Sgonzo rid = 1; 822239281Sgonzo isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 823239281Sgonzo if (!isc->uhh_mem_res) { 824239281Sgonzo device_printf(dev, "Error: Could not map UHH memory\n"); 825239281Sgonzo goto error; 826239281Sgonzo } 827239281Sgonzo /* Allocate resource for the TLL register set */ 828239281Sgonzo rid = 2; 829239281Sgonzo isc->tll_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 830239281Sgonzo if (!isc->tll_mem_res) { 831239281Sgonzo device_printf(dev, "Error: Could not map TLL memory\n"); 832239281Sgonzo goto error; 833239281Sgonzo } 834239281Sgonzo 835239281Sgonzo /* Add this device as a child of the USBus device */ 836239281Sgonzo sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 837239281Sgonzo if (!sc->sc_bus.bdev) { 838239281Sgonzo device_printf(dev, "Error: could not add USB device\n"); 839239281Sgonzo goto error; 840239281Sgonzo } 841239281Sgonzo 842239281Sgonzo device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 843239281Sgonzo device_set_desc(sc->sc_bus.bdev, OMAP_EHCI_HC_DEVSTR); 844239281Sgonzo 845239281Sgonzo /* Set the vendor name */ 846239281Sgonzo sprintf(sc->sc_vendor, "Texas Instruments"); 847239281Sgonzo 848239281Sgonzo /* Get the GPIO device, we may need this if the driver needs to toggle 849239281Sgonzo * some pins for external PHY resets. 850239281Sgonzo */ 851239281Sgonzo isc->sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 852239281Sgonzo if (isc->sc_gpio_dev == NULL) { 853239281Sgonzo device_printf(dev, "Error: failed to get the GPIO device\n"); 854239281Sgonzo goto error; 855239281Sgonzo } 856239281Sgonzo 857239281Sgonzo /* Set the defaults for the hints */ 858239281Sgonzo for (i = 0; i < 3; i++) { 859239281Sgonzo isc->phy_reset[i] = 0; 860239281Sgonzo isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; 861239281Sgonzo isc->reset_gpio_pin[i] = -1; 862239281Sgonzo } 863239281Sgonzo 864239281Sgonzo tuple_size = sizeof(pcell_t) * 3; 865239281Sgonzo node = ofw_bus_get_node(dev); 866239281Sgonzo len = OF_getprop(node, "phy-config", phyconf, sizeof(phyconf)); 867239281Sgonzo if (len > 0) { 868239281Sgonzo if (len % tuple_size) 869239281Sgonzo goto error; 870239281Sgonzo if ((len / tuple_size) != 3) 871239281Sgonzo goto error; 872239281Sgonzo 873239281Sgonzo phyconf_ptr = phyconf; 874239281Sgonzo for (i = 0; i < 3; i++) { 875239281Sgonzo isc->port_mode[i] = fdt32_to_cpu(*phyconf_ptr); 876239281Sgonzo isc->phy_reset[i] = fdt32_to_cpu(*(phyconf_ptr + 1)); 877239281Sgonzo isc->reset_gpio_pin[i] = fdt32_to_cpu(*(phyconf_ptr + 2)); 878239281Sgonzo 879239281Sgonzo phyconf_ptr += 3; 880239281Sgonzo } 881239281Sgonzo } 882239281Sgonzo 883239281Sgonzo /* Initialise the ECHI registers */ 884239281Sgonzo err = omap_ehci_init(isc); 885239281Sgonzo if (err) { 886239281Sgonzo device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); 887239281Sgonzo goto error; 888239281Sgonzo } 889239281Sgonzo 890239281Sgonzo 891239281Sgonzo /* Set the tag and size of the register set in the EHCI context */ 892239281Sgonzo sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 893239281Sgonzo sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 894239281Sgonzo sc->sc_io_size = rman_get_size(sc->sc_io_res); 895239281Sgonzo 896239281Sgonzo 897239281Sgonzo /* Setup the interrupt */ 898239281Sgonzo err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 899239281Sgonzo NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 900239281Sgonzo if (err) { 901239281Sgonzo device_printf(dev, "Error: could not setup irq, %d\n", err); 902239281Sgonzo sc->sc_intr_hdl = NULL; 903239281Sgonzo goto error; 904239281Sgonzo } 905239281Sgonzo 906239281Sgonzo 907239281Sgonzo /* Finally we are ready to kick off the ECHI host controller */ 908239281Sgonzo err = ehci_init(sc); 909239281Sgonzo if (err == 0) { 910239281Sgonzo err = device_probe_and_attach(sc->sc_bus.bdev); 911239281Sgonzo } 912239281Sgonzo if (err) { 913239281Sgonzo device_printf(dev, "Error: USB init failed err=%d\n", err); 914239281Sgonzo goto error; 915239281Sgonzo } 916239281Sgonzo 917239281Sgonzo return (0); 918239281Sgonzo 919239281Sgonzoerror: 920239281Sgonzo omap_ehci_detach(dev); 921239281Sgonzo return (ENXIO); 922239281Sgonzo} 923239281Sgonzo 924239281Sgonzo/** 925239281Sgonzo * omap_ehci_detach - detach the device and cleanup the driver 926239281Sgonzo * @dev: device handle 927239281Sgonzo * 928239281Sgonzo * Clean-up routine where everything initialised in omap_ehci_attach is 929239281Sgonzo * freed and cleaned up. This function calls omap_ehci_fini() to shutdown 930239281Sgonzo * the on-chip module. 931239281Sgonzo * 932239281Sgonzo * LOCKING: 933239281Sgonzo * none 934239281Sgonzo * 935239281Sgonzo * RETURNS: 936239281Sgonzo * Always returns 0 (success). 937239281Sgonzo */ 938239281Sgonzostatic int 939239281Sgonzoomap_ehci_detach(device_t dev) 940239281Sgonzo{ 941239281Sgonzo struct omap_ehci_softc *isc = device_get_softc(dev); 942239281Sgonzo ehci_softc_t *sc = &isc->base; 943239281Sgonzo device_t bdev; 944239281Sgonzo int err; 945239281Sgonzo 946239281Sgonzo if (sc->sc_bus.bdev) { 947239281Sgonzo bdev = sc->sc_bus.bdev; 948239281Sgonzo device_detach(bdev); 949239281Sgonzo device_delete_child(dev, bdev); 950239281Sgonzo } 951239281Sgonzo 952239281Sgonzo /* during module unload there are lots of children leftover */ 953239281Sgonzo device_delete_children(dev); 954239281Sgonzo 955239281Sgonzo /* 956239281Sgonzo * disable interrupts that might have been switched on in ehci_init 957239281Sgonzo */ 958239281Sgonzo if (sc->sc_io_res) { 959239281Sgonzo EWRITE4(sc, EHCI_USBINTR, 0); 960239281Sgonzo } 961239281Sgonzo 962239281Sgonzo if (sc->sc_irq_res && sc->sc_intr_hdl) { 963239281Sgonzo /* 964239281Sgonzo * only call ehci_detach() after ehci_init() 965239281Sgonzo */ 966239281Sgonzo ehci_detach(sc); 967239281Sgonzo 968239281Sgonzo err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); 969239281Sgonzo if (err) 970239281Sgonzo device_printf(dev, "Error: could not tear down irq, %d\n", err); 971239281Sgonzo sc->sc_intr_hdl = NULL; 972239281Sgonzo } 973239281Sgonzo 974239281Sgonzo /* Free the resources stored in the base EHCI handler */ 975239281Sgonzo if (sc->sc_irq_res) { 976239281Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 977239281Sgonzo sc->sc_irq_res = NULL; 978239281Sgonzo } 979239281Sgonzo if (sc->sc_io_res) { 980239281Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); 981239281Sgonzo sc->sc_io_res = NULL; 982239281Sgonzo } 983239281Sgonzo 984239281Sgonzo /* Release the other register set memory maps */ 985239281Sgonzo if (isc->tll_mem_res) { 986239281Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->tll_mem_res); 987239281Sgonzo isc->tll_mem_res = NULL; 988239281Sgonzo } 989239281Sgonzo if (isc->uhh_mem_res) { 990239281Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res); 991239281Sgonzo isc->uhh_mem_res = NULL; 992239281Sgonzo } 993239281Sgonzo 994239281Sgonzo usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 995239281Sgonzo 996239281Sgonzo omap_ehci_fini(isc); 997239281Sgonzo 998239281Sgonzo return (0); 999239281Sgonzo} 1000239281Sgonzo 1001239281Sgonzostatic device_method_t ehci_methods[] = { 1002239281Sgonzo /* Device interface */ 1003239281Sgonzo DEVMETHOD(device_probe, omap_ehci_probe), 1004239281Sgonzo DEVMETHOD(device_attach, omap_ehci_attach), 1005239281Sgonzo DEVMETHOD(device_detach, omap_ehci_detach), 1006239281Sgonzo DEVMETHOD(device_suspend, omap_ehci_suspend), 1007239281Sgonzo DEVMETHOD(device_resume, omap_ehci_resume), 1008239281Sgonzo DEVMETHOD(device_shutdown, omap_ehci_shutdown), 1009239281Sgonzo 1010239281Sgonzo /* Bus interface */ 1011239281Sgonzo DEVMETHOD(bus_print_child, bus_generic_print_child), 1012239281Sgonzo 1013239281Sgonzo {0, 0} 1014239281Sgonzo}; 1015239281Sgonzo 1016239281Sgonzostatic driver_t ehci_driver = { 1017239281Sgonzo "ehci", 1018239281Sgonzo ehci_methods, 1019239281Sgonzo sizeof(struct omap_ehci_softc), 1020239281Sgonzo}; 1021239281Sgonzo 1022239281Sgonzostatic devclass_t ehci_devclass; 1023239281Sgonzo 1024239281SgonzoDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 1025