diff options
Diffstat (limited to 'drivers/usb/host/ehci-tegra.c')
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 4ddb279cfb35..095447d9e3a2 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -23,12 +23,16 @@ #include <mach/usb_phy.h> #include <mach/iomap.h> +#include "../../../arch/arm/mach-tegra/tegra_usb_phy.h" + #if 0 #define EHCI_DBG(stuff...) pr_info("ehci-tegra: " stuff) #else #define EHCI_DBG(stuff...) do {} while (0) #endif +#define TEGRA_USB_PORTSC1_PFSC (1 << 24) + static const char driver_name[] = "tegra-ehci"; #define TEGRA_USB_DMA_ALIGN 32 @@ -51,6 +55,22 @@ struct dma_align_buffer { u8 data[0]; }; +#ifdef CONFIG_MACH_COLIBRI_T20 +/* To limit the speed of USB to full speed */ +int g_usb_high_speed = 0; + +/* To limit the speed of USB to full speed */ +static int __init enable_usb_high_speed(char *s) +{ + if (!(*s) || !strcmp(s, "1")) + g_usb_high_speed = 1; + + return 0; +} +__setup("usb_high_speed=", enable_usb_high_speed); +EXPORT_SYMBOL_GPL(g_usb_high_speed); +#endif /* CONFIG_MACH_COLIBRI_T20 */ + static void free_align_buffer(struct urb *urb) { struct dma_align_buffer *temp = container_of(urb->transfer_buffer, @@ -186,6 +206,71 @@ static irqreturn_t tegra_ehci_irq(struct usb_hcd *hcd) return irq_status; } +static int tegra_ehci_internal_port_reset( + struct ehci_hcd *ehci, + u32 __iomem *portsc_reg +) +{ + u32 temp; + unsigned long flags; + int retval = 0; + int i, tries; + u32 saved_usbintr; + + spin_lock_irqsave(&ehci->lock, flags); + saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable); + /* disable USB interrupt */ + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + spin_unlock_irqrestore(&ehci->lock, flags); + + /* + * Here we have to do Port Reset at most twice for + * Port Enable bit to be set. + */ + for (i = 0; i < 2; i++) { + temp = ehci_readl(ehci, portsc_reg); + temp |= PORT_RESET; + ehci_writel(ehci, temp, portsc_reg); + mdelay(10); + temp &= ~PORT_RESET; + ehci_writel(ehci, temp, portsc_reg); + mdelay(1); + tries = 100; + do { + mdelay(1); + /* + * Up to this point, Port Enable bit is + * expected to be set after 2 ms waiting. + * USB1 usually takes extra 45 ms, for safety, + * we take 100 ms as timeout. + */ + temp = ehci_readl(ehci, portsc_reg); + } while (!(temp & PORT_PE) && tries--); + if (temp & PORT_PE) + break; + } + if (i == 2) + retval = -ETIMEDOUT; + + /* + * Clear Connect Status Change bit if it's set. + * We can't clear PORT_PEC. It will also cause PORT_PE to be cleared. + */ + if (temp & PORT_CSC) + ehci_writel(ehci, PORT_CSC, portsc_reg); + + /* + * Write to clear any interrupt status bits that might be set + * during port reset. + */ + temp = ehci_readl(ehci, &ehci->regs->status); + ehci_writel(ehci, temp, &ehci->regs->status); + + /* restore original interrupt enable bits */ + ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable); + + return retval; +} static int tegra_ehci_hub_control( struct usb_hcd *hcd, @@ -201,6 +286,29 @@ static int tegra_ehci_hub_control( int retval = 0; u32 __iomem *status_reg; +#ifdef CONFIG_MACH_COLIBRI_T20 + u32 temp; + + /* To limit the speed of USB to full speed */ + if (!g_usb_high_speed) { + /* Check whether port is not 2nd one internally connected to + ASIX Ethernet chip, set PFSC (Port Force Full Speed) only + for externally accessible OTG and host port */ + if (tegra->phy->inst != 1) { + status_reg = &ehci->regs->port_status[(wIndex & 0xff) + - 1]; + temp = ehci_readl(ehci, status_reg); + /* Check whether PFSC bit is already set or not */ + if (!(temp & TEGRA_USB_PORTSC1_PFSC)) { + ehci_writel(ehci, (temp | + TEGRA_USB_PORTSC1_PFSC), + status_reg); + temp = ehci_readl(ehci, status_reg); + } + } + } +#endif /* CONFIG_MACH_COLIBRI_T20 */ + if (!tegra_usb_phy_hw_accessible(tegra->phy)) { if (buf) memset(buf, 0, wLength); @@ -249,6 +357,13 @@ static int tegra_ehci_hub_control( break; } + /* For USB1 port we need to issue Port Reset twice internally */ + if (tegra->phy->inst == 0 && + (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { + status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; + return tegra_ehci_internal_port_reset(ehci, status_reg); + } + /* handle ehci hub control request */ retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); |