From db2f0ea2994604a962442aaa761327f642b25db0 Mon Sep 17 00:00:00 2001 From: Gilles Chanteperdrix Date: Mon, 9 May 2011 23:04:50 +0200 Subject: omap4: add support for EHCI As board may need some specific support, implement inner functions to be called by the boards implementaions of ehci_hcd_start, ehci_hcd_stop. Signed-off-by: Gilles Chanteperdrix --- arch/arm/include/asm/arch-omap4/ehci.h | 8 + drivers/usb/host/Makefile | 1 + drivers/usb/host/ehci-omap4.c | 268 +++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 arch/arm/include/asm/arch-omap4/ehci.h create mode 100644 drivers/usb/host/ehci-omap4.c diff --git a/arch/arm/include/asm/arch-omap4/ehci.h b/arch/arm/include/asm/arch-omap4/ehci.h new file mode 100644 index 000000000..fc84f6aa0 --- /dev/null +++ b/arch/arm/include/asm/arch-omap4/ehci.h @@ -0,0 +1,8 @@ +#ifndef EHCI_H +#define EHCI_H + +int omap4_ehci_hcd_init(void); + +int omap4_ehci_hcd_stop(void); + +#endif /* EHCI_H */ diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 51b249432..5a5b971c5 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -46,6 +46,7 @@ COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o COBJS-$(CONFIG_USB_EHCI_KIRKWOOD) += ehci-kirkwood.o COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o +COBJS-$(CONFIG_USB_EHCI_OMAP4) += ehci-omap4.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/usb/host/ehci-omap4.c b/drivers/usb/host/ehci-omap4.c new file mode 100644 index 000000000..3a8d4b68f --- /dev/null +++ b/drivers/usb/host/ehci-omap4.c @@ -0,0 +1,268 @@ +/* + * OMAP4 EHCI port, copied from linux/drivers/usb/host/ehci-omap.c + * + * Copyright (C) 2007-2010 Texas Instruments, Inc. + * Author: Vikram Pandita + * Author: Anand Gadiyar + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" +#include "ehci-core.h" + +#define EHCI_BASE (OMAP44XX_L4_CORE_BASE + 0x64C00) +#define UHH_BASE (OMAP44XX_L4_CORE_BASE + 0x64000) +#define TLL_BASE (OMAP44XX_L4_CORE_BASE + 0x62000) + +/* ULPI */ +#define ULPI_SET(a) (a + 1) +#define ULPI_CLR(a) (a + 2) + +#define ULPI_FUNC_CTRL 0x04 + +#define ULPI_FUNC_CTRL_RESET (1 << 5) + +/* TLL Register Set */ +#define OMAP_USBTLL_REVISION (0x00) +#define OMAP_USBTLL_SYSCONFIG (0x10) +#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_USBTLL_SYSSTATUS (0x14) +#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP_USBTLL_IRQSTATUS (0x18) +#define OMAP_USBTLL_IRQENABLE (0x1C) + +#define OMAP_TLL_SHARED_CONF (0x30) +#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6) +#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5) +#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2) +#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1) +#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0) + +#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) +#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) +#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) +#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) +#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) +#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) + +#define OMAP_TLL_ULPI_FUNCTION_CTRL(num) (0x804 + 0x100 * num) +#define OMAP_TLL_ULPI_INTERFACE_CTRL(num) (0x807 + 0x100 * num) +#define OMAP_TLL_ULPI_OTG_CTRL(num) (0x80A + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_RISE(num) (0x80D + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_FALL(num) (0x810 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_STATUS(num) (0x813 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_LATCH(num) (0x814 + 0x100 * num) +#define OMAP_TLL_ULPI_DEBUG(num) (0x815 + 0x100 * num) +#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num) + +#define OMAP_TLL_CHANNEL_COUNT 3 +#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 1) +#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 2) +#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 4) + +/* UHH Register Set */ +#define OMAP_UHH_REVISION (0x00) +#define OMAP_UHH_SYSCONFIG (0x10) +#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12) +#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_UHH_SYSSTATUS (0x14) +#define OMAP_UHH_HOSTCONFIG (0x40) +#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11) +#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12) +#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2) +#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3) +#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4) +#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5) +#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8) +#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) +#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) + +/* OMAP4-specific defines */ +#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2) +#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2) + +#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4) +#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4) +#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0) + +#define OMAP4_P1_MODE_CLEAR (3 << 16) +#define OMAP4_P1_MODE_TLL (1 << 16) +#define OMAP4_P1_MODE_HSIC (3 << 16) +#define OMAP4_P2_MODE_CLEAR (3 << 18) +#define OMAP4_P2_MODE_TLL (1 << 18) +#define OMAP4_P2_MODE_HSIC (3 << 18) + +#define OMAP_REV2_TLL_CHANNEL_COUNT 2 + +#define OMAP_UHH_DEBUG_CSR (0x44) + +/* EHCI Register Set */ +#define EHCI_INSNREG04 (0xA0) +#define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5) +#define EHCI_INSNREG05_ULPI (0xA4) +#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31 +#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24 +#define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22 +#define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16 +#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 +#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 + +int omap4_ehci_hcd_init(void) +{ + unsigned long base = get_timer(0); + unsigned reg = 0, port = 0; + int rc; + + /* USB host, with clock from external phy as port 1 UTMI clock */ + sr32((void *)0x4A009358, 0, 32, 0x01000002); + + /* FSUSB clk */ + sr32((void *)0x4a0093d0, 0, 32, 0x2); + + /* USB TLL clock */ + sr32((void *)0x4a009368, 0, 32, 0x1); + + /* enable the 32K, 48M optional clocks and enable the module */ + sr32((void *)0x4a0093e0, 0, 32, 0x301); + + /* perform TLL soft reset, and wait until reset is complete */ + writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET, + TLL_BASE + OMAP_USBTLL_SYSCONFIG); + + /* Wait for TLL reset to complete */ + while (!(readl(TLL_BASE + OMAP_USBTLL_SYSSTATUS) + & OMAP_USBTLL_SYSSTATUS_RESETDONE)) + if (get_timer(base) > CONFIG_SYS_HZ) { + printf("OMAP4 EHCI error: timeout resetting TLL\n"); + return -1; + } + + writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | + OMAP_USBTLL_SYSCONFIG_SIDLEMODE | + OMAP_USBTLL_SYSCONFIG_CACTIVITY, + TLL_BASE + OMAP_USBTLL_SYSCONFIG); + + /* Put UHH in NoIdle/NoStandby mode */ + reg = readl(UHH_BASE + OMAP_UHH_SYSCONFIG); + reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; + reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; + writel(reg, UHH_BASE + OMAP_UHH_SYSCONFIG); + + reg = readl(UHH_BASE + OMAP_UHH_HOSTCONFIG); + + /* setup ULPI bypass and burst configurations */ + reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); + reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; + + /* Clear port mode fields for PHY mode*/ + reg &= ~OMAP4_P1_MODE_CLEAR; + reg &= ~OMAP4_P2_MODE_CLEAR; + writel(reg, UHH_BASE + OMAP_UHH_HOSTCONFIG); + + /* + * An undocumented "feature" in the OMAP3 EHCI controller, + * causes suspended ports to be taken out of suspend when + * the USBCMD.Run/Stop bit is cleared (for example when + * we do ehci_bus_suspend). + * This breaks suspend-resume if the root-hub is allowed + * to suspend. Writing 1 to this undocumented register bit + * disables this feature and restores normal behavior. + */ + writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, EHCI_BASE + EHCI_INSNREG04); + + reg = ULPI_FUNC_CTRL_RESET + /* FUNCTION_CTRL_SET register */ + | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT) + /* Write */ + | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) + /* PORTn */ + | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) + /* start ULPI access*/ + | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT); + + base = get_timer(0); + + writel(reg, EHCI_BASE + EHCI_INSNREG05_ULPI); + + /* Wait for ULPI access completion */ + while ((readl(EHCI_BASE + EHCI_INSNREG05_ULPI) + & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) + if (get_timer(base) > CONFIG_SYS_HZ) { + printf("OMAP4 EHCI error: timeout resetting phy\n"); + return -1; + } + + hccr = (struct ehci_hccr *)(EHCI_BASE); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + return 0; +} + +int omap4_ehci_hcd_stop(void) +{ + unsigned base = get_timer(0); + + writel(OMAP4_UHH_SYSCONFIG_SOFTRESET, UHH_BASE + OMAP_UHH_SYSCONFIG); + +#if 0 + /* We get timeout here */ + while (!(readl(UHH_BASE + OMAP_UHH_SYSSTATUS) & (1 << 0))) + if (get_timer(base) > CONFIG_SYS_HZ) { + printf("OMAP4 EHCI error: reset UHH 0 timeout\n"); + return -ETIMEDOUT; + } + + while (!(readl(UHH_BASE + OMAP_UHH_SYSSTATUS) & (1 << 1))) + if (get_timer(base) > CONFIG_SYS_HZ) { + printf("OMAP4 EHCI error: reset UHH 1 timeout\n"); + return -ETIMEDOUT; + } + + while (!(readl(UHH_BASE + OMAP_UHH_SYSSTATUS) & (1 << 2))) + if (get_timer(base) > CONFIG_SYS_HZ) { + printf("OMAP4 EHCI error: reset UHH 2 timeout\n"); + return -ETIMEDOUT; + } +#endif + + + writel((1 << 1), TLL_BASE + OMAP_USBTLL_SYSCONFIG); + + while (!(readl(TLL_BASE + OMAP_USBTLL_SYSSTATUS) & (1 << 0))) + if (get_timer(base) > CONFIG_SYS_HZ) { + printf("OMAP4 EHCI error: reset TLL timeout\n"); + return -ETIMEDOUT; + } + + /* Disable clocks */ + sr32((void *)0x4a0093e0, 0, 32, 0); + sr32((void *)0x4a009368, 0, 32, 0); + sr32((void *)0x4a0093d0, 0, 32, 0); + sr32((void *)0x4A009358, 0, 32, 0); + + return 0; +} -- cgit v1.2.3