diff options
author | Seshendra Gadagottu <sgadagottu@nvidia.com> | 2011-03-01 10:19:28 +0530 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-03-01 16:16:35 -0800 |
commit | ea85e0c7a3a8a8b70478befb1fd7c9ff1d69821b (patch) | |
tree | f676fd36e86259f016032a53ef739a8b6949f7ab | |
parent | 46fad22837d5b873704a8e422d4966d2ff72d99d (diff) |
tegra: usb: ehci: Error handling for hsic phy rx error
Implementation of recover function for hsic phy rx error.
BUG 791857
Change-Id: Iedd16caf8ce61a9fa280ae3f62d4f1475318ee72
Reviewed-on: http://git-master/r/21225
Tested-by: Seshendra Gadagottu <sgadagottu@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 97 |
1 files changed, 94 insertions, 3 deletions
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 560ac6ae477f..9adcfef2af07 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -25,12 +25,22 @@ #include <linux/irq.h> #include <linux/usb/otg.h> #include <mach/usb_phy.h> +#include <mach/iomap.h> #define TEGRA_USB_USBCMD_REG_OFFSET 0x140 #define TEGRA_USB_USBCMD_RESET (1 << 1) #define TEGRA_USB_USBMODE_REG_OFFSET 0x1a8 #define TEGRA_USB_USBMODE_HOST (3 << 0) #define TEGRA_USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) +#define TEGRA_USB_PORTSC_PHCD (1 << 23) + +#define TEGRA_USB_SUSP_CTRL_OFFSET 0x400 +#define TEGRA_USB_SUSP_CLR (1 << 5) +#define TEGRA_USB_PHY_CLK_VALID (1 << 7) +#define TEGRA_USB_SRT (1 << 25) + +#define TEGRA_LVL2_CLK_GATE_OVRB 0xfc +#define TEGRA_USB2_CLK_OVR_ON (1 << 10) #define TEGRA_USB_DMA_ALIGN 32 @@ -671,10 +681,9 @@ static void tegra_hsic_connection_work(struct work_struct *work) return; } - - #ifdef CONFIG_USB_EHCI_ONOFF_FEATURE -struct usb_hcd *ehci_handle; /* For ehci on/off sysfs */ +/* Stored ehci handle for hsic insatnce */ +struct usb_hcd *ehci_handle; int ehci_tegra_irq; static ssize_t show_ehci_power(struct device *dev, @@ -727,6 +736,88 @@ static inline void remove_ehci_sys_file(struct ehci_hcd *ehci) device_remove_file(ehci_to_hcd(ehci)->self.controller, &dev_attr_ehci_power); } + +static int ehci_tegra_wait_register(void __iomem *reg, u32 mask, u32 result) +{ + unsigned long timeout = 2000; + + do { + if ((readl(reg) & mask) == result) + return 0; + udelay(1); + timeout--; + } while (timeout); + return -1; +} + + +void tegra_ehci_recover_rx_error(void) +{ + struct ehci_hcd *ehci; + unsigned long val; + struct usb_hcd *hcd = ehci_handle; + + if (hcd) { + ehci = hcd_to_ehci(ehci_handle); + pr_info("{ RX_ERR_HANDLING_START \n"); + /* (0) set CLK_RST_..._LVL2_CLK_GATE_OVRB_0 USB2_CLK_OVR_ON = 1 */ + val = readl((IO_ADDRESS(TEGRA_CLK_RESET_BASE) + TEGRA_LVL2_CLK_GATE_OVRB)); + val |= TEGRA_USB2_CLK_OVR_ON; + writel(val, (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + TEGRA_LVL2_CLK_GATE_OVRB)); + /* (1) set PORTSC SUSP = 1 */ + val = ehci_readl(ehci, &ehci->regs->port_status[0]); + ehci_writel(ehci, val | PORT_SUSPEND, &ehci->regs->port_status[0]); + /* (2) wait until PORTSC SUSP = 1 */ + if (handshake(ehci, &ehci->regs->port_status[0], PORT_SUSPEND, + PORT_SUSPEND, 2000)) { + pr_err("%s: timeout waiting for PORT_SUSPEND = 1\n", __func__); + return; + } + /* (3) set PORTSC PHCD = 1 */ + val = ehci_readl(ehci, &ehci->regs->port_status[0]); + ehci_writel(ehci, val | TEGRA_USB_PORTSC_PHCD, &ehci->regs->port_status[0]); + /* (4) wait until SUSP_CTRL PHY_VALID = 0 */ + if (ehci_tegra_wait_register(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET, + TEGRA_USB_PHY_CLK_VALID, 0) < 0) { + pr_err("%s: timeout waiting for TEGRA_USB_PHY_CLK_VALID = 0\n", __func__); + return; + } + /* (5) set SUSP_CTRL SUSP_CLR = 1 */ + val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); + writel((val | TEGRA_USB_SUSP_CLR), + (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); + /* (6) set SUSP_CTRL SUSP_CLR = 0 */ + val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); + val &= ~(TEGRA_USB_SUSP_CLR); + writel(val, (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); + /* (7) wait until SUSP_CTRL PHY_VALID = 1 */ + if (ehci_tegra_wait_register(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET, + TEGRA_USB_PHY_CLK_VALID, TEGRA_USB_PHY_CLK_VALID) < 0) { + pr_err("%s: timeout waiting for TEGRA_USB_PHY_CLK_VALID = 1\n", __func__); + return; + } + /* (8) set PORTSC SRT = 1 */ + val = ehci_readl(ehci, &ehci->regs->port_status[0]); + ehci_writel(ehci, val | TEGRA_USB_SRT, &ehci->regs->port_status[0]); + /* (9) set PORTSC FPR = 1 */ + val = ehci_readl(ehci, &ehci->regs->port_status[0]); + ehci_writel(ehci, val | PORT_RESUME, &ehci->regs->port_status[0]); + /* (10) wait until PORTSC FPR = 0 */ + if (handshake(ehci, &ehci->regs->port_status[0], PORT_RESUME, + 0, 2000)) { + pr_err("%s: timeout waiting for PORT_RESUME = 1\n", __func__); + return; + } + /* (11) set PORTSC SRT = 0 */ + val = ehci_readl(ehci, &ehci->regs->port_status[0]); + val &= ~(TEGRA_USB_SRT); + ehci_writel(ehci, val, &ehci->regs->port_status[0]); + pr_info("} \n"); + } +} + +EXPORT_SYMBOL(tegra_ehci_recover_rx_error); + #endif static const struct hc_driver tegra_ehci_hc_driver = { |