uboot/drivers/usb/host/ehci-omap.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2011 Ilya Yanok, Emcraft Systems
   3 * (C) Copyright 2004-2008
   4 * Texas Instruments, <www.ti.com>
   5 *
   6 * Derived from Beagle Board code by
   7 *      Sunil Kumar <sunilsaini05@gmail.com>
   8 *      Shashi Ranjan <shashiranjanmca05@gmail.com>
   9 *
  10 *
  11 * See file CREDITS for list of people who contributed to this
  12 * project.
  13 *
  14 * This program is free software; you can redistribute it and/or
  15 * modify it under the terms of the GNU General Public License as
  16 * published by the Free Software Foundation; either version 2 of
  17 * the License, or (at your option) any later version.
  18 *
  19 * This program is distributed in the hope that it will be useful,
  20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22 * GNU General Public License for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License
  25 * along with this program; if not, write to the Free Software
  26 * Foundation, Inc.
  27 */
  28#include <common.h>
  29#include <usb.h>
  30#include <usb/ulpi.h>
  31#include <errno.h>
  32#include <asm/io.h>
  33#include <asm/gpio.h>
  34#include <asm/arch/ehci.h>
  35#include <asm/ehci-omap.h>
  36
  37#include "ehci.h"
  38
  39static struct omap_uhh *const uhh = (struct omap_uhh *)OMAP_UHH_BASE;
  40static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE;
  41static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE;
  42
  43static int omap_uhh_reset(void)
  44{
  45        unsigned long init = get_timer(0);
  46
  47        /* perform UHH soft reset, and wait until reset is complete */
  48        writel(OMAP_UHH_SYSCONFIG_SOFTRESET, &uhh->sysc);
  49
  50        /* Wait for UHH reset to complete */
  51        while (!(readl(&uhh->syss) & OMAP_UHH_SYSSTATUS_EHCI_RESETDONE))
  52                if (get_timer(init) > CONFIG_SYS_HZ) {
  53                        debug("OMAP UHH error: timeout resetting ehci\n");
  54                        return -EL3RST;
  55                }
  56
  57        return 0;
  58}
  59
  60static int omap_ehci_tll_reset(void)
  61{
  62        unsigned long init = get_timer(0);
  63
  64        /* perform TLL soft reset, and wait until reset is complete */
  65        writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET, &usbtll->sysc);
  66
  67        /* Wait for TLL reset to complete */
  68        while (!(readl(&usbtll->syss) & OMAP_USBTLL_SYSSTATUS_RESETDONE))
  69                if (get_timer(init) > CONFIG_SYS_HZ) {
  70                        debug("OMAP EHCI error: timeout resetting TLL\n");
  71                        return -EL3RST;
  72        }
  73
  74        return 0;
  75}
  76
  77static void omap_usbhs_hsic_init(int port)
  78{
  79        unsigned int reg;
  80
  81        /* Enable channels now */
  82        reg = readl(&usbtll->channel_conf + port);
  83
  84        setbits_le32(&reg, (OMAP_TLL_CHANNEL_CONF_CHANMODE_TRANSPARENT_UTMI
  85                | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
  86                | OMAP_TLL_CHANNEL_CONF_DRVVBUS
  87                | OMAP_TLL_CHANNEL_CONF_CHRGVBUS
  88                | OMAP_TLL_CHANNEL_CONF_CHANEN));
  89
  90        writel(reg, &usbtll->channel_conf + port);
  91}
  92
  93static void omap_ehci_soft_phy_reset(int port)
  94{
  95        struct ulpi_viewport ulpi_vp;
  96
  97        ulpi_vp.viewport_addr = (u32)&ehci->insreg05_utmi_ulpi;
  98        ulpi_vp.port_num = port;
  99
 100        ulpi_reset(&ulpi_vp);
 101}
 102
 103inline int __board_usb_init(void)
 104{
 105        return 0;
 106}
 107int board_usb_init(void) __attribute__((weak, alias("__board_usb_init")));
 108
 109#if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \
 110        defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO)
 111/* controls PHY(s) reset signal(s) */
 112static inline void omap_ehci_phy_reset(int on, int delay)
 113{
 114        /*
 115         * Refer ISSUE1:
 116         * Hold the PHY in RESET for enough time till
 117         * PHY is settled and ready
 118         */
 119        if (delay && !on)
 120                udelay(delay);
 121#ifdef CONFIG_OMAP_EHCI_PHY1_RESET_GPIO
 122        gpio_request(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, "USB PHY1 reset");
 123        gpio_direction_output(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, !on);
 124#endif
 125#ifdef CONFIG_OMAP_EHCI_PHY2_RESET_GPIO
 126        gpio_request(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, "USB PHY2 reset");
 127        gpio_direction_output(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, !on);
 128#endif
 129
 130        /* Hold the PHY in RESET for enough time till DIR is high */
 131        /* Refer: ISSUE1 */
 132        if (delay && on)
 133                udelay(delay);
 134}
 135#else
 136#define omap_ehci_phy_reset(on, delay)  do {} while (0)
 137#endif
 138
 139/* Reset is needed otherwise the kernel-driver will throw an error. */
 140int omap_ehci_hcd_stop(void)
 141{
 142        debug("Resetting OMAP EHCI\n");
 143        omap_ehci_phy_reset(1, 0);
 144
 145        if (omap_uhh_reset() < 0)
 146                return -1;
 147
 148        if (omap_ehci_tll_reset() < 0)
 149                return -1;
 150
 151        return 0;
 152}
 153
 154/*
 155 * Initialize the OMAP EHCI controller and PHY.
 156 * Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1
 157 * See there for additional Copyrights.
 158 */
 159int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata,
 160                struct ehci_hccr **hccr, struct ehci_hcor **hcor)
 161{
 162        int ret;
 163        unsigned int i, reg = 0, rev = 0;
 164
 165        debug("Initializing OMAP EHCI\n");
 166
 167        ret = board_usb_init();
 168        if (ret < 0)
 169                return ret;
 170
 171        /* Put the PHY in RESET */
 172        omap_ehci_phy_reset(1, 10);
 173
 174        ret = omap_uhh_reset();
 175        if (ret < 0)
 176                return ret;
 177
 178        ret = omap_ehci_tll_reset();
 179        if (ret)
 180                return ret;
 181
 182        writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
 183                OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
 184                OMAP_USBTLL_SYSCONFIG_CACTIVITY, &usbtll->sysc);
 185
 186        /* Put UHH in NoIdle/NoStandby mode */
 187        writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc);
 188
 189        /* setup ULPI bypass and burst configurations */
 190        clrsetbits_le32(&reg, OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN,
 191                (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN |
 192                OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN |
 193                OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN));
 194
 195        rev = readl(&uhh->rev);
 196        if (rev == OMAP_USBHS_REV1) {
 197                if (is_ehci_phy_mode(usbhs_pdata->port_mode[0]))
 198                        clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
 199                else
 200                        setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
 201
 202                if (is_ehci_phy_mode(usbhs_pdata->port_mode[1]))
 203                        clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
 204                else
 205                        setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
 206
 207                if (is_ehci_phy_mode(usbhs_pdata->port_mode[2]))
 208                        clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
 209                else
 210                        setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
 211        } else if (rev == OMAP_USBHS_REV2) {
 212                clrsetbits_le32(&reg, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR),
 213                                        OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
 214
 215                /* Clear port mode fields for PHY mode*/
 216
 217                if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
 218                        setbits_le32(&reg, OMAP_P1_MODE_HSIC);
 219
 220                if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
 221                        setbits_le32(&reg, OMAP_P2_MODE_HSIC);
 222
 223                if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2]))
 224                        setbits_le32(&reg, OMAP_P3_MODE_HSIC);
 225        }
 226
 227        debug("OMAP UHH_REVISION 0x%x\n", rev);
 228        writel(reg, &uhh->hostconfig);
 229
 230        for (i = 0; i < OMAP_HS_USB_PORTS; i++)
 231                if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i]))
 232                        omap_usbhs_hsic_init(i);
 233
 234        omap_ehci_phy_reset(0, 10);
 235
 236        /*
 237         * An undocumented "feature" in the OMAP3 EHCI controller,
 238         * causes suspended ports to be taken out of suspend when
 239         * the USBCMD.Run/Stop bit is cleared (for example when
 240         * we do ehci_bus_suspend).
 241         * This breaks suspend-resume if the root-hub is allowed
 242         * to suspend. Writing 1 to this undocumented register bit
 243         * disables this feature and restores normal behavior.
 244         */
 245        writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, &ehci->insreg04);
 246
 247        for (i = 0; i < OMAP_HS_USB_PORTS; i++)
 248                if (is_ehci_phy_mode(usbhs_pdata->port_mode[i]))
 249                        omap_ehci_soft_phy_reset(i);
 250
 251        *hccr = (struct ehci_hccr *)(OMAP_EHCI_BASE);
 252        *hcor = (struct ehci_hcor *)(OMAP_EHCI_BASE + 0x10);
 253
 254        debug("OMAP EHCI init done\n");
 255        return 0;
 256}
 257