blob: 94aa356dd3f7b4be112af50bc1f3b119e0f6c4be [file] [log] [blame]
/*
* Copyright (C) 2010 Michael Grzeschik <mgr@pengutronix.de>
*
* This file is released under the GPLv2
*
*/
/*
* OMAP USBHOST Register addresses: VIRTUAL ADDRESSES
*/
/*-------------------------------------------------------------------------*/
#include <mfd/twl4030.h>
#include <usb/twl4030.h>
#include <mach/ehci.h>
#include <common.h>
#include <asm/io.h>
#include <clock.h>
#include <gpio.h>
#include <mach/omap3-silicon.h>
#include <mach/omap3-clock.h>
#include <mach/cm-regbits-34xx.h>
#include <mach/sys_info.h>
void omap_usb_utmi_init(struct omap_hcd *omap, u8 tll_channel_mask)
{
unsigned reg;
int i;
/* Program the 3 TLL channels upfront */
for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
reg = __raw_readl(OMAP_USBTLL_BASE + OMAP_TLL_CHANNEL_CONF(i));
/* Disable AutoIdle, BitStuffing and use SDR Mode */
reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
__raw_writel(reg, OMAP_USBTLL_BASE + OMAP_TLL_CHANNEL_CONF(i));
}
/* Program Common TLL register */
reg = __raw_readl(OMAP_USBTLL_BASE + OMAP_TLL_SHARED_CONF);
reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON
| OMAP_TLL_SHARED_CONF_USB_DIVRATION
| OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN);
reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN;
__raw_writel(reg, OMAP_USBTLL_BASE + OMAP_TLL_SHARED_CONF);
/* Enable channels now */
for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
reg = __raw_readl(OMAP_USBTLL_BASE + OMAP_TLL_CHANNEL_CONF(i));
/* Enable only the reg that is needed */
if (!(tll_channel_mask & 1<<i))
continue;
reg |= OMAP_TLL_CHANNEL_CONF_CHANEN;
__raw_writel(reg, OMAP_USBTLL_BASE + OMAP_TLL_CHANNEL_CONF(i));
__raw_writeb(0xbe,
OMAP_USBTLL_BASE + OMAP_TLL_ULPI_SCRATCH_REGISTER(i));
}
}
int ehci_omap_init(struct omap_hcd *omap)
{
uint64_t start;
int timeout = 1000;
u8 tll_ch_mask = 0;
u32 v = 0;
if (twl4030_usb_ulpi_init()) {
printf("ERROR: %s Could not initialize PHY\n",
__PRETTY_FUNCTION__);
return -EINVAL;
}
v = __raw_readl(CM_REG(CLKSEL4_PLL));
v |= (12 << OMAP3430ES2_PERIPH2_DPLL_DIV_SHIFT);
v |= (120 << OMAP3430ES2_PERIPH2_DPLL_MULT_SHIFT);
__raw_writel(v, CM_REG(CLKSEL4_PLL));
v = __raw_readl(CM_REG(CLKSEL5_PLL));
v |= (1 << OMAP3430ES2_DIV_120M_SHIFT);
__raw_writel(v, CM_REG(CLKSEL5_PLL));
v = __raw_readl(CM_REG(CLKEN2_PLL));
v |= (7 << OMAP3430ES2_PERIPH2_DPLL_FREQSEL_SHIFT);
v |= (7 << OMAP3430ES2_EN_PERIPH2_DPLL_SHIFT);
__raw_writel(v, CM_REG(CLKEN2_PLL));
/* PRCM settings for USBHOST:
* Interface clk un-related to domain transition
*/
v = __raw_readl(CM_REG(AIDLE_USBH));
v |= (0 << OMAP3430ES2_AUTO_USBHOST_SHIFT);
__raw_writel(v, CM_REG(AIDLE_USBH));
/* Disable sleep dependency with MPU and IVA */
v = __raw_readl(CM_REG(SLEEPD_USBH));
v |= (0 << OMAP3430ES2_EN_MPU_SHIFT);
v |= (0 << OMAP3430ES2_EN_IVA2_SHIFT);
__raw_writel(v, CM_REG(SLEEPD_USBH));
/* Disable Automatic transition of clock */
v = __raw_readl(CM_REG(CLKSTCTRL_USBH));
v |= (0 << OMAP3430ES2_CLKTRCTRL_USBHOST_SHIFT);
__raw_writel(v, CM_REG(CLKSTCTRL_USBH));
/* Enable Clocks for USBHOST */
/* enable usbhost_ick */
v = __raw_readl(CM_REG(ICLKEN_USBH));
v |= (1 << OMAP3430ES2_EN_USBHOST_SHIFT);
__raw_writel(v, CM_REG(ICLKEN_USBH));
/* enable usbhost_120m_fck */
v = __raw_readl(CM_REG(FCLKEN_USBH));
v |= (1 << OMAP3430ES2_EN_USBHOST2_SHIFT);
__raw_writel(v, CM_REG(FCLKEN_USBH));
/* enable usbhost_48m_fck */
v = __raw_readl(CM_REG(FCLKEN_USBH));
v |= (1 << OMAP3430ES2_EN_USBHOST1_SHIFT);
__raw_writel(v, CM_REG(FCLKEN_USBH));
if (omap->phy_reset) {
/* Refer: ISSUE1 */
if (omap->reset_gpio_port[0] != -EINVAL) {
gpio_direction_output(omap->reset_gpio_port[0], 0);
}
if (omap->reset_gpio_port[1] != -EINVAL) {
gpio_direction_output(omap->reset_gpio_port[1], 0);
}
/* Hold the PHY in RESET for enough time till DIR is high */
mdelay(10);
}
/* enable usbtll_fck */
v = __raw_readl(CM_REG(FCLKEN3_CORE));
v |= (1 << OMAP3430ES2_EN_USBTLL_SHIFT);
__raw_writel(v, CM_REG(FCLKEN3_CORE));
/* Configure TLL for 60Mhz clk for ULPI */
/* enable usbtll_ick */
v = __raw_readl(CM_REG(ICLKEN3_CORE));
v |= (1 << OMAP3430ES2_EN_USBTLL_SHIFT);
__raw_writel(v, CM_REG(ICLKEN3_CORE));
v = __raw_readl(CM_REG(AIDLE3_CORE));
v |= (0 << OMAP3430ES2_AUTO_USBTLL_SHIFT);
__raw_writel(v, CM_REG(AIDLE3_CORE));
/* perform TLL soft reset, and wait until reset is complete */
__raw_writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET,
OMAP_USBTLL_BASE + OMAP_USBTLL_SYSCONFIG);
/* Wait for TLL reset to complete */
start = get_time_ns();
while (!(__raw_readl(OMAP_USBTLL_BASE + OMAP_USBTLL_SYSSTATUS)
& OMAP_USBTLL_SYSSTATUS_RESETDONE)) {
if (is_timeout(start, timeout * USECOND)) {
return -ETIMEDOUT;
}
}
/* (1<<3) = no idle mode only for initial debugging */
__raw_writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
OMAP_USBTLL_SYSCONFIG_CACTIVITY, OMAP_USBTLL_BASE + OMAP_USBTLL_SYSCONFIG);
/* Put UHH in NoIdle/NoStandby mode */
v = __raw_readl(OMAP_UHH_CONFIG_BASE + OMAP_UHH_SYSCONFIG);
v |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
| OMAP_UHH_SYSCONFIG_SIDLEMODE
| OMAP_UHH_SYSCONFIG_CACTIVITY
| OMAP_UHH_SYSCONFIG_MIDLEMODE);
v &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
__raw_writel(v, OMAP_UHH_CONFIG_BASE + OMAP_UHH_SYSCONFIG);
v = __raw_readl(OMAP_UHH_CONFIG_BASE + OMAP_UHH_HOSTCONFIG);
/* setup ULPI bypass and burst configurations */
v |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
| OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
| OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
v &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
v &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
v &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
v &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
/* Bypass the TLL module for PHY mode operation */
if (get_cpu_rev() <= OMAP34XX_ES2_1) {
if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
(omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
(omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
v &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
else
v |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
} else {
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
v &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
v |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
v &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
v |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)
v &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
v |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
}
__raw_writel(v, OMAP_UHH_CONFIG_BASE + OMAP_UHH_HOSTCONFIG);
if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) ||
(omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) ||
(omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) {
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
tll_ch_mask |= OMAP_TLL_CHANNEL_1_EN_MASK;
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
tll_ch_mask |= OMAP_TLL_CHANNEL_2_EN_MASK;
if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK;
/* Enable UTMI mode for required TLL channels */
omap_usb_utmi_init(omap, tll_ch_mask);
}
if (omap->phy_reset) {
/* Refer ISSUE1:
* Hold the PHY in RESET for enough time till
* PHY is settled and ready
*/
udelay(10);
if (omap->reset_gpio_port[0] != -EINVAL)
gpio_direction_output(omap->reset_gpio_port[0], 1);
if (omap->reset_gpio_port[1] != -EINVAL)
gpio_direction_output(omap->reset_gpio_port[1], 1);
}
return 0;
}