diff options
author | Michael Grzeschik <m.grzeschik@pengutronix.de> | 2010-08-04 11:59:15 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2010-08-06 19:17:45 +0200 |
commit | e503c22837279d65497c6967665b424dbc650f31 (patch) | |
tree | 74a3e7d449efafb0cce46d5222d63efdeff8333f /drivers | |
parent | f1329a15007f5636e3ecd2b0ab5f388da20ff5c5 (diff) | |
download | barebox-e503c22837279d65497c6967665b424dbc650f31.tar.gz barebox-e503c22837279d65497c6967665b424dbc650f31.tar.xz |
ehci: add omap support
with cm-regbits-34xx.h and codebase from linux kernel
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/Kconfig | 4 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-omap.c | 260 |
3 files changed, 265 insertions, 0 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index e1549e8186..145f7d1c69 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -1,2 +1,6 @@ config USB_EHCI bool "EHCI driver" + +config USB_EHCI_OMAP + depends on ARCH_OMAP3 + bool "OMAP EHCI driver" diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6c48e3aa39..a334b08115 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_USB_EHCI) += ehci-hcd.o +obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c new file mode 100644 index 0000000000..0c30c52fc9 --- /dev/null +++ b/drivers/usb/host/ehci-omap.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2010 Michael Grzeschik <mgr@pengutronix.de> + * + * This file is released under the GPLv2 + * + */ + +/* + * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES + */ + +/*-------------------------------------------------------------------------*/ + +#include <i2c/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() <= CPU_ES2P1) { + 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; +} |