diff options
author | Rakesh Bodla <rbodla@nvidia.com> | 2012-05-22 11:31:14 +0530 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-05-24 14:49:49 -0700 |
commit | 4e8c072c5b697b9893273d054c82e12026cf2907 (patch) | |
tree | 1bc57de78f53443e119d93498c0ac68a2c2b36e6 /drivers | |
parent | eeb3cd3b539cf11d998f0a656b64bd8728de0656 (diff) |
usb: host: tegra: update ehci to use common phy
Following enhancements are done:
a. Update driver with common phy interface
b. Make host driver independent of phy type and
remove unnecessary CONFIG variables.
Bug 887361
Change-Id: Ibafa37a048df4377b73029039b04d04a53020bd4
Signed-off-by: Rakesh Bodla <rbodla@nvidia.com>
Reviewed-on: http://git-master/r/103599
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 1376 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 |
3 files changed, 264 insertions, 1125 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 25ed607aab9a..bf171c180987 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -145,7 +145,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, spin_lock_irqsave(&ehci->lock, flags); /* clear phy low-power mode before changing wakeup flags */ - if (ehci->has_hostpc) { + if (ehci->has_hostpc && !ehci->broken_hostpc_phcd) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg; @@ -181,7 +181,7 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, } /* enter phy low-power mode again */ - if (ehci->has_hostpc) { + if (ehci->has_hostpc && !ehci->broken_hostpc_phcd) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg; @@ -285,7 +285,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } } #ifdef CONFIG_ARCH_TEGRA_2x_SOC - if (changed && ehci->has_hostpc) { + if (changed && ehci->has_hostpc && !ehci->broken_hostpc_phcd) { spin_unlock_irq(&ehci->lock); msleep(5); /* 5 ms for HCD to enter low-power mode */ spin_lock_irq(&ehci->lock); @@ -389,7 +389,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); /* clear phy low-power mode before resume */ - if (ehci->bus_suspended && ehci->has_hostpc) { + if (ehci->bus_suspended && ehci->has_hostpc && !ehci->broken_hostpc_phcd) { i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { @@ -731,7 +731,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (hostpc_reg) { + if (hostpc_reg && !ehci->broken_hostpc_phcd) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -979,7 +979,7 @@ static int ehci_hub_control ( temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (hostpc_reg) { + if (hostpc_reg && !ehci->broken_hostpc_phcd) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 4e7277b2b889..82523bd200a6 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -2,7 +2,7 @@ * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs * * Copyright (C) 2010 Google, Inc. - * Copyright (C) 2009 - 2012 NVIDIA Corporation + * Copyright (C) 2009 - 2011 NVIDIA Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,7 +16,6 @@ * */ -#include <linux/clk.h> #include <linux/platform_device.h> #include <linux/platform_data/tegra_usb.h> #include <linux/irq.h> @@ -24,191 +23,146 @@ #include <mach/usb_phy.h> #include <mach/iomap.h> -#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_USB_PHY_CLK_VALID_INT_ENB (1 << 9) -#define TEGRA_USB_PHY_CLK_VALID_INT_STS (1 << 8) - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -#define TEGRA_USB_PORTSC1_OFFSET 0x184 -#else -#define TEGRA_USB_PORTSC1_OFFSET 0x174 -#endif -#define TEGRA_USB_PORTSC1_WKCN (1 << 20) - -#define TEGRA_LVL2_CLK_GATE_OVRB 0xfc -#define TEGRA_USB2_CLK_OVR_ON (1 << 10) +static const char driver_name[] = "tegra-ehci"; #define TEGRA_USB_DMA_ALIGN 32 -#define STS_SRI (1<<7) /* SOF Recieved */ - -#define HOSTPC_REG_OFFSET 0x1b4 - -#define HOSTPC1_DEVLC_STS (1 << 28) -#define HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) - -#define USB1_PREFETCH_ID 6 -#define USB2_PREFETCH_ID 18 -#define USB3_PREFETCH_ID 17 - struct tegra_ehci_hcd { struct ehci_hcd *ehci; struct tegra_usb_phy *phy; - struct clk *clk; - struct clk *emc_clk; - struct clk *sclk_clk; +#ifdef CONFIG_USB_OTG_UTILS struct otg_transceiver *transceiver; - int host_resumed; - int bus_suspended; - int port_resuming; - int power_down_on_bus_suspend; - int default_enable; - enum tegra_usb_phy_port_speed port_speed; - struct work_struct clk_timer_work; - struct timer_list clk_timer; - bool clock_enabled; - bool timer_event; - struct mutex tegra_ehci_hcd_mutex; +#endif + struct mutex sync_lock; + bool port_resuming; unsigned int irq; bool bus_suspended_fail; }; -static void tegra_ehci_power_up(struct usb_hcd *hcd, bool is_dpd) +struct dma_align_buffer { + void *kmalloc_ptr; + void *old_xfer_buffer; + u8 data[0]; +}; + +static void free_align_buffer(struct urb *urb) { - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct dma_align_buffer *temp = container_of(urb->transfer_buffer, + struct dma_align_buffer, data); + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; + + /* In transaction, DMA from Device */ + if (usb_urb_dir_in(urb)) + memcpy(temp->old_xfer_buffer, temp->data, + urb->transfer_buffer_length); - if (!tegra->default_enable) - clk_enable(tegra->clk); - tegra_usb_phy_power_on(tegra->phy, is_dpd); - tegra->host_resumed = 1; + urb->transfer_buffer = temp->old_xfer_buffer; + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; + kfree(temp->kmalloc_ptr); } -static void tegra_ehci_power_down(struct usb_hcd *hcd, bool is_dpd) +static int alloc_align_buffer(struct urb *urb, gfp_t mem_flags) { - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct dma_align_buffer *temp, *kmalloc_ptr; + size_t kmalloc_size; + + if (urb->num_sgs || urb->sg || + urb->transfer_buffer_length == 0 || + !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) + return 0; + + /* Allocate a buffer with enough padding for alignment */ + kmalloc_size = urb->transfer_buffer_length + + sizeof(struct dma_align_buffer) + TEGRA_USB_DMA_ALIGN - 1; + kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); + + if (!kmalloc_ptr) + return -ENOMEM; - tegra->host_resumed = 0; - tegra_usb_phy_power_off(tegra->phy, is_dpd); - if (!tegra->default_enable) - clk_disable(tegra->clk); + /* Position our struct dma_align_buffer such that data is aligned */ + temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; + temp->kmalloc_ptr = kmalloc_ptr; + temp->old_xfer_buffer = urb->transfer_buffer; + /* OUT transaction, DMA to Device */ + if (!usb_urb_dir_in(urb)) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + + urb->transfer_buffer = temp->data; + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; + + return 0; } -static int tegra_ehci_internal_port_reset( - struct ehci_hcd *ehci, - u32 __iomem *portsc_reg -) +static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, + struct urb *urb, gfp_t mem_flags) { - u32 temp; - unsigned long flags; - int retval = 0; - int i, tries; - u32 saved_usbintr; + int ret; - 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); + ret = alloc_align_buffer(urb, mem_flags); + if (ret) + return ret; - /* - * 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; + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + + /* Control packets over dma */ + if (urb->setup_dma) + dma_sync_single_for_device(hcd->self.controller, + urb->setup_dma, sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + + /* urb buffers over dma */ + if (urb->transfer_dma) { + enum dma_data_direction dir; + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + dma_sync_single_for_device(hcd->self.controller, + urb->transfer_dma, urb->transfer_buffer_length, dir); } - 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; + + if (ret) + free_align_buffer(urb); + + return ret; } -static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) +static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, + struct urb *urb) +{ + usb_hcd_unmap_urb_for_dma(hcd, urb); + free_align_buffer(urb); + + if (urb->transfer_dma) { + enum dma_data_direction dir; + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + if (dir == DMA_FROM_DEVICE) + dma_sync_single_for_cpu(hcd->self.controller, + urb->transfer_dma, urb->transfer_buffer_length, + DMA_FROM_DEVICE); + } +} + +static irqreturn_t tegra_ehci_irq(struct usb_hcd *hcd) { - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - struct ehci_regs __iomem *hw = ehci->regs; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - u32 val; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); irqreturn_t irq_status; bool pmc_remote_wakeup = false; - /* Fence read for coherency of AHB master intiated writes */ - if (tegra->phy->instance == 0) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); - else if (tegra->phy->instance == 1) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB2_PREFETCH_ID)); - else if (tegra->phy->instance == 2) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB3_PREFETCH_ID)); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc)) { - /* check if there is any remote wake event */ - if (tegra_usb_phy_is_remotewake_detected(tegra->phy)) { - pmc_remote_wakeup = true; - spin_lock (&ehci->lock); - usb_hcd_resume_root_hub(hcd); - spin_unlock (&ehci->lock); - } - } - if (tegra->phy->hotplug) { - spin_lock(&ehci->lock); - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - if ((val & TEGRA_USB_PHY_CLK_VALID_INT_STS)) { - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB | - TEGRA_USB_PHY_CLK_VALID_INT_STS; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - - val = readl(&hw->status); - if (!(val & STS_PCD)) { - spin_unlock(&ehci->lock); - return 0; - } - val = readl(hcd->regs + TEGRA_USB_PORTSC1_OFFSET); - val &= ~(TEGRA_USB_PORTSC1_WKCN | PORT_RWC_BITS); - writel(val , (hcd->regs + TEGRA_USB_PORTSC1_OFFSET)); - } + spin_lock(&ehci->lock); + irq_status = tegra_usb_phy_irq(tegra->phy); + if (irq_status == IRQ_NONE) { spin_unlock(&ehci->lock); + return irq_status; } + if (tegra_usb_phy_remote_wakeup(tegra->phy)) { + ehci_info(ehci, "remote wakeup detected\n"); + pmc_remote_wakeup = true; + usb_hcd_resume_root_hub(hcd); + } + spin_unlock(&ehci->lock); irq_status = ehci_irq(hcd); @@ -220,561 +174,112 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) ehci->controller_remote_wakeup = false; /* disable interrupts */ ehci_writel(ehci, 0, &ehci->regs->intr_enable); - tegra_usb_phy_preresume(tegra->phy, true); + tegra_usb_phy_pre_resume(tegra->phy, true); tegra->port_resuming = 1; } return irq_status; } + static int tegra_ehci_hub_control( struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength ) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int ports = HCS_N_PORTS(ehci->hcs_params); - u32 temp, status, cmd_run; - u32 __iomem *status_reg; - u32 usbsts_reg; - + struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; - int retval = 0; - unsigned selector; - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - bool hsic = false; + int retval = 0; + u32 __iomem *status_reg; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if (!tegra->host_resumed) { + if (!tegra_usb_phy_hw_accessible(tegra->phy)) { if (buf) - memset (buf, 0, wLength); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + memset(buf, 0, wLength); return retval; } - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; spin_lock_irqsave(&ehci->lock, flags); - - /* - * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits - * that are write on clear, by writing back the register read value, so - * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits - */ - if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { - temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; - ehci_writel(ehci, temp & ~PORT_PE, status_reg); - goto done; - } else if (typeReq == GetPortStatus) { - temp = ehci_readl(ehci, status_reg); - /* check port is in resume state */ - if (tegra->port_resuming) { - int delay = ehci->reset_done[wIndex-1] - jiffies; - /* Sometimes it seems we get called too soon... In that case, wait.*/ - if (delay > 0) { - ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); - mdelay(jiffies_to_msecs(delay)); - } - /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ - if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { - pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); + /* Do tegra phy specific actions based on the type request */ + switch (typeReq) { + case GetPortStatus: + if (time_after_eq(jiffies, ehci->reset_done[wIndex - 1])) { + if (tegra->port_resuming) { + int delay = ehci->reset_done[wIndex-1] - jiffies; + /* Sometimes it seems we get called too soon... In that case, wait.*/ + if (delay > 0) { + ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay); + mdelay(jiffies_to_msecs(delay)); + } + /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */ + if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) { + pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__); + } + tegra_usb_phy_post_resume(tegra->phy); + tegra->port_resuming = 0; + /* If run bit is not set by now enable it */ + if (ehci->command & CMD_RUN) { + ehci->command |= CMD_RUN; + ehci_writel(ehci, ehci->command, + &ehci->regs->command); + } } - tegra->port_resuming = 0; - tegra_usb_phy_postresume(tegra->phy, false); - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - ehci->command |= CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run |= CMD_RUN; - /* - * ehci run bit is disabled to avoid SOF. - * 2LS WAR is executed by now enable the run bit. - */ - ehci_writel(ehci, cmd_run, &ehci->regs->command); - /* Now we can safely re-enable irqs */ - ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); - } - } - - } else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { - temp = ehci_readl(ehci, status_reg); - if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { - retval = -EPIPE; - goto done; } - - temp &= ~PORT_WKCONN_E; - temp |= PORT_WKDISC_E | PORT_WKOC_E; - ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - - /* Need a 4ms delay before the controller goes to suspend */ - mdelay(4); - - /* - * If a transaction is in progress, there may be a delay in - * suspending the port. Poll until the port is suspended. - */ - if (handshake(ehci, status_reg, PORT_SUSPEND, - PORT_SUSPEND, 5000)) - pr_err("%s: timeout waiting for SUSPEND\n", __func__); - - set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); - /* - * If RUN bit is disabled interrupt is not generated after suspend. - * This change on T20 will allow ASE interrupt generated after suspend - * which will unlink the qheads. - */ -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* Disable RUN bit. */ - ehci->command &= ~CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run &= ~CMD_RUN; - ehci_writel(ehci, cmd_run, &ehci->regs->command); - if (handshake (ehci, &ehci->regs->status, - STS_HALT, STS_HALT, 16 * 125)) - pr_err("%s() timeout waiting for STS_HALT\n", __func__); + break; + case ClearPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + tegra_usb_phy_pre_resume(tegra->phy, false); + tegra->port_resuming = 1; } -#endif - tegra_usb_phy_postsuspend(tegra->phy, false); - - goto done; - } - - /* For USB1 port we need to issue Port Reset twice internally */ - if (tegra->phy->instance == 0 && - (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { - spin_unlock_irqrestore(&ehci->lock, flags); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return tegra_ehci_internal_port_reset(ehci, status_reg); + break; } + spin_unlock_irqrestore(&ehci->lock, flags); - /* - * Tegra host controller will time the resume operation to clear the bit - * when the port control state switches to HS or FS Idle. This behavior - * is different from EHCI where the host controller driver is required - * to set this bit to a zero after the resume duration is timed in the - * driver. - */ - else if (typeReq == ClearPortFeature && - wValue == USB_PORT_FEAT_SUSPEND) { - temp = ehci_readl(ehci, status_reg); - if ((temp & PORT_RESET) || !(temp & PORT_PE)) { - retval = -EPIPE; - goto done; - } - - if (!(temp & PORT_SUSPEND)) - goto done; - - tegra->port_resuming = 1; - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* disable interrupts */ - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - /* Disable RUN bit. */ - ehci->command &= ~CMD_RUN; - cmd_run = ehci_readl(ehci, &ehci->regs->command); - cmd_run &= ~CMD_RUN; - ehci_writel(ehci, cmd_run, &ehci->regs->command); - if (handshake (ehci, &ehci->regs->status, - STS_HALT, STS_HALT, 16 * 125)) - pr_err("%s() timeout waiting for STS_HALT\n", __func__); - } - - /* Disable disconnect detection during port resume */ - tegra_usb_phy_preresume(tegra->phy, false); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_UTMIP) { -#endif - ehci_dbg(ehci, "%s:USBSTS = 0x%x", __func__, - ehci_readl(ehci, &ehci->regs->status)); - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, usbsts_reg, &ehci->regs->status); - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - udelay(20); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) - pr_err("%s: timeout set for STS_SRI\n", __func__); - - usbsts_reg = ehci_readl(ehci, &ehci->regs->status); - ehci_writel(ehci, usbsts_reg, &ehci->regs->status); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, 0, 2000)) - pr_err("%s: timeout clear STS_SRI\n", __func__); - - if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) - pr_err("%s: timeout set STS_SRI\n", __func__); - - udelay(20); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - } -#endif - temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); - /* start resume signaling */ - ehci_writel(ehci, temp | PORT_RESUME, status_reg); - - ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); - /* whoever resumes must GetPortStatus to complete it!! */ - goto done; - } + /* handle ehci hub control request */ + retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); - /* Handle port reset here */ - if ((hsic) && (typeReq == SetPortFeature) && - ((wValue == USB_PORT_FEAT_RESET) || (wValue == USB_PORT_FEAT_POWER))) { - selector = wIndex >> 8; - wIndex &= 0xff; - if (!wIndex || wIndex > ports) { - retval = -EPIPE; - goto done; - } - wIndex--; - status = 0; - temp = ehci_readl(ehci, status_reg); - if (temp & PORT_OWNER) - goto done; - temp &= ~PORT_RWC_BITS; - - switch (wValue) { - case USB_PORT_FEAT_RESET: - { - if (temp & PORT_RESUME) { - retval = -EPIPE; - goto done; - } - /* line status bits may report this as low speed, - * which can be fine if this root hub has a - * transaction translator built in. - */ - if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT - && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { - ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); - temp |= PORT_OWNER; - ehci_writel(ehci, temp, status_reg); - } else { - ehci_vdbg(ehci, "port %d reset\n", wIndex + 1); - temp &= ~PORT_PE; - /* - * caller must wait, then call GetPortStatus - * usb 2.0 spec says 50 ms resets on root - */ - ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(50); - ehci_writel(ehci, temp, status_reg); - if (hsic && (wIndex == 0)) + /* do tegra phy specific actions based on the type request */ + if (!retval) { + spin_lock_irqsave(&ehci->lock, flags); + switch (typeReq) { + case SetPortFeature: + if (wValue == USB_PORT_FEAT_SUSPEND) { + /* Need a 4ms delay for controller to suspend */ + mdelay(4); + tegra_usb_phy_post_suspend(tegra->phy); + } else if (wValue == USB_PORT_FEAT_RESET) { + if (ehci->reset_done[0] && wIndex == 0) tegra_usb_phy_bus_reset(tegra->phy); + } else if (wValue == USB_PORT_FEAT_POWER) { + if (wIndex == 1) + tegra_usb_phy_port_power(tegra->phy); } - break; } - case USB_PORT_FEAT_POWER: - { - if (HCS_PPC(ehci->hcs_params)) - ehci_writel(ehci, temp | PORT_POWER, status_reg); - if (hsic && (wIndex == 0)) - tegra_usb_phy_bus_connect(tegra->phy); - break; - } - } - goto done; + spin_unlock_irqrestore(&ehci->lock, flags); } - spin_unlock_irqrestore(&ehci->lock, flags); - - /* Handle the hub control events here */ - retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return retval; -done: - spin_unlock_irqrestore(&ehci->lock, flags); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); return retval; } -#ifdef CONFIG_PM -static void tegra_ehci_restart(struct usb_hcd *hcd, bool is_dpd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - unsigned int temp; - - ehci->controller_resets_phy = 0; - tegra_ehci_pre_reset(tegra->phy, false); - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) - ehci->controller_resets_phy = 1; - - /* setup the frame list and Async q heads */ - ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); - ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); - /* setup the command register and set the controller in RUN mode */ - ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - /* dont start RS here for HSIC, it will be set by bus_reset */ - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_HSIC) -#endif - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - - /* Enable the root Port Power */ - if (HCS_PPC(ehci->hcs_params)) { - temp = ehci_readl(ehci, &ehci->regs->port_status[0]); - ehci_writel(ehci, temp | PORT_POWER, &ehci->regs->port_status[0]); - } - - down_write(&ehci_cf_port_reset_rwsem); - if(is_dpd) - hcd->state = HC_STATE_SUSPENDED; - else - hcd->state = HC_STATE_RUNNING; - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - /* flush posted writes */ - ehci_readl(ehci, &ehci->regs->command); - up_write(&ehci_cf_port_reset_rwsem); - - /* Turn On Interrupts */ - ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); -} - -static int tegra_usb_suspend(struct usb_hcd *hcd, bool is_dpd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_regs __iomem *hw = tegra->ehci->regs; - unsigned long flags; - int hsic = 0; - - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - - spin_lock_irqsave(&tegra->ehci->lock, flags); - - if (tegra->ehci->has_hostpc) - tegra->port_speed = (readl(hcd->regs + HOSTPC_REG_OFFSET) >> 25) & 0x3; - else - tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; - ehci_halt(tegra->ehci); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - /* - * Ehci run bit is disabled by now read this into command variable - * so that bus resume will not enable run bit immedialty. - * this is required for 2LS WAR on UTMIP interface. - */ - tegra->ehci->command = ehci_readl(tegra->ehci, - &tegra->ehci->regs->command); - } -#endif - - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - tegra_ehci_power_down(hcd, is_dpd); - return 0; -} - -static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct ehci_regs __iomem *hw = ehci->regs; - unsigned long val; - bool hsic; - bool null_ulpi; - bool utmip_remote_wakeup = false; - - null_ulpi = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI); - hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); - - tegra_ehci_power_up(hcd, is_dpd); - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - if ((tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) || (hsic) || - (null_ulpi)) - goto restart; - - /* Force the phy to keep data lines in suspend state */ - tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { - ehci_reset(ehci); - } - - /* Enable host mode */ - tdi_reset(ehci); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc)) { - val = readl(hcd->regs + HOSTPC_REG_OFFSET); - val &= ~HOSTPC1_DEVLC_PTS(~0); - val |= HOSTPC1_DEVLC_STS; - writel(val, hcd->regs + HOSTPC_REG_OFFSET); - } - - /* Enable Port Power */ - val = readl(&hw->port_status[0]); - val |= PORT_POWER; - writel(val, &hw->port_status[0]); - udelay(10); - - if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && - (tegra->ehci->has_hostpc) && (tegra->phy->remote_wakeup)) { - utmip_remote_wakeup = true; - } - - /* Check if the phy resume from LP0. When the phy resume from LP0 - * USB register will be reset. */ - if (!readl(&hw->async_next)) { -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Start the controller */ - val = readl(&hw->command); - writel((val | CMD_RUN), &hw->command); -#endif - /* Program the field PTC based on the saved speed mode */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH) - val |= PORT_TEST_FORCE; - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) - val |= PORT_TEST(6); - else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= PORT_TEST(7); - writel(val, &hw->port_status[0]); - udelay(10); - - /* Disable test mode by setting PTC field to NORMAL_OP */ - val = readl(&hw->port_status[0]); - val &= ~PORT_TEST(~0); - writel(val, &hw->port_status[0]); - udelay(10); - } - - /* Poll until CCS is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, - PORT_CONNECT, 2000)) { - pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); - goto restart; - } - - /* Poll until PE is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_PE, - PORT_PE, 2000)) { - pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); - goto restart; - } - - /* Clear the PCI status, to avoid an interrupt taken upon resume */ - val = readl(&hw->status); - val |= STS_PCD; - writel(val, &hw->status); - - /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */ - val = readl(&hw->port_status[0]); - if ((val & PORT_POWER) && (val & PORT_PE)) { - val |= PORT_SUSPEND; - writel(val, &hw->port_status[0]); - - /* Need a 4ms delay before the controller goes to suspend */ - mdelay(4); - - /* Wait until port suspend completes */ - if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, - PORT_SUSPEND, 1000)) { - pr_err("%s: timeout waiting for PORT_SUSPEND\n", - __func__); - goto restart; - } - } - - tegra_ehci_phy_restore_end(tegra->phy); - if (utmip_remote_wakeup) { - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - } - return 0; - -restart: - if (null_ulpi) { - bool LP0 = !readl(&hw->async_next); - - if (LP0) { - static int cnt = 1; - - pr_info("LP0 restart %d\n", cnt++); - tegra_ehci_phy_restore_start(tegra->phy, - tegra->port_speed); - } - - val = readl(&hw->port_status[0]); - if (!((val & PORT_POWER) && (val & PORT_PE))) { - tegra_ehci_restart(hcd, is_dpd); - } - - if (LP0) - tegra_ehci_phy_restore_end(tegra->phy); - - return 0; - } - - if ((tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) && (!hsic)) - tegra_ehci_phy_restore_end(tegra->phy); - if (hsic) { - val = readl(&hw->port_status[0]); - if (!((val & PORT_POWER) && (val & PORT_PE))) - tegra_ehci_restart(hcd, false); - - tegra_usb_phy_bus_idle(tegra->phy); - if (!tegra_usb_phy_is_device_connected(tegra->phy)) - pr_err("%s: no hsic device conenction\n", __func__); - } else { - tegra_ehci_restart(hcd, false); - } - - return 0; -} -#endif - -/* - * Disable PHY clock valid interrupts and wait for the interrupt handler to - * finish. - * - * Requires a lock on tegra_ehci_hcd_mutex - * Must not be called with a lock on ehci->lock - */ -static void tegra_ehci_disable_phy_interrupt(struct usb_hcd *hcd) { - struct tegra_ehci_hcd *tegra; - u32 val; - if (hcd->irq >= 0) { - tegra = dev_get_drvdata(hcd->self.controller); - if (tegra->phy->hotplug) { - /* Disable PHY clock valid interrupts */ - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - } - /* Wait for the interrupt handler to finish */ - synchronize_irq(hcd->irq); - } -} - static void tegra_ehci_shutdown(struct usb_hcd *hcd) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - tegra_ehci_disable_phy_interrupt(hcd); - /* ehci_shutdown touches the USB controller registers, make sure - * controller has clocks to it */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, false); - - ehci_shutdown(hcd); - - /* we are ready to shut down, powerdown the phy */ - tegra_ehci_power_down(hcd, false); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + mutex_lock(&tegra->sync_lock); + del_timer_sync(&ehci->watchdog); + del_timer_sync(&ehci->iaa_watchdog); + if (tegra_usb_phy_hw_accessible(tegra->phy)) { + spin_lock_irq(&ehci->lock); + ehci_silence_controller(ehci); + spin_unlock_irq(&ehci->lock); + } + mutex_unlock(&tegra->sync_lock); } static int tegra_ehci_setup(struct usb_hcd *hcd) @@ -786,24 +291,18 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci->has_hostpc = tegra_usb_phy_has_hostpc(tegra->phy) ? 1 : 0; + ehci->broken_hostpc_phcd = true; -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - ehci->has_hostpc = 1; -#endif hcd->has_tt = 1; - if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_NULL_ULPI) { - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - } - retval = ehci_halt(ehci); if (retval) return retval; @@ -815,267 +314,46 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) ehci->sbrn = 0x20; ehci->controller_remote_wakeup = false; - - if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) { - tegra_ehci_pre_reset(tegra->phy, false); - ehci_reset(ehci); - tegra_ehci_post_reset(tegra->phy, false); - - /* - * Resetting the controller has the side effect of resetting the PHY. - * So, never reset the controller after the calling - * tegra_ehci_reinit API. - */ - ehci->controller_resets_phy = 1; - } + ehci_reset(ehci); + tegra_usb_phy_reset(tegra->phy); ehci_port_power(ehci, 1); return retval; } + #ifdef CONFIG_PM static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - int error_status = 0; + int err = 0; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); + mutex_lock(&tegra->sync_lock); tegra->bus_suspended_fail = false; - tegra_ehci_disable_phy_interrupt(hcd); - /* ehci_shutdown touches the USB controller registers, make sure - * controller has clocks to it */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, false); - error_status = ehci_bus_suspend(hcd); - if (error_status) + err = ehci_bus_suspend(hcd); + if (err) tegra->bus_suspended_fail = true; - if (!error_status && tegra->power_down_on_bus_suspend) { - tegra_usb_suspend(hcd, false); - tegra->bus_suspended = 1; - } - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + else + tegra_usb_phy_suspend(tegra->phy); + mutex_unlock(&tegra->sync_lock); - return error_status; + return err; } static int tegra_ehci_bus_resume(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - int ehci_bus_resumed; + int err = 0; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if (tegra->bus_suspended && tegra->power_down_on_bus_suspend) { - tegra_usb_resume(hcd, false); - tegra->bus_suspended = 0; - } + mutex_lock(&tegra->sync_lock); + tegra_usb_phy_resume(tegra->phy); + err = ehci_bus_resume(hcd); + mutex_unlock(&tegra->sync_lock); - ehci_bus_resumed = ehci_bus_resume(hcd); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ehci_bus_resumed; + return err; } #endif -struct dma_aligned_buffer { - void *kmalloc_ptr; - void *old_xfer_buffer; - u8 data[0]; -}; - -static void free_dma_aligned_buffer(struct urb *urb) -{ - struct dma_aligned_buffer *temp = container_of(urb->transfer_buffer, - struct dma_aligned_buffer, data); - - if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) - return; - - if(usb_urb_dir_in(urb)) - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); - urb->transfer_buffer = temp->old_xfer_buffer; - kfree(temp->kmalloc_ptr); - urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; -} - -static int alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags) -{ - struct dma_aligned_buffer *temp, *kmalloc_ptr; - size_t kmalloc_size; - - if (urb->num_sgs || urb->sg || - urb->transfer_buffer_length == 0 || - !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) - return 0; - - /* Allocate a buffer with enough padding for alignment */ - kmalloc_size = urb->transfer_buffer_length + - sizeof(struct dma_aligned_buffer) + TEGRA_USB_DMA_ALIGN - 1; - - kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); - if (!kmalloc_ptr) - return -ENOMEM; - - /* Position our struct dma_aligned_buffer such that data is aligned */ - temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; - temp->kmalloc_ptr = kmalloc_ptr; - temp->old_xfer_buffer = urb->transfer_buffer; - if (!usb_urb_dir_in(urb)) - memcpy(temp->data, urb->transfer_buffer, - urb->transfer_buffer_length); - urb->transfer_buffer = temp->data; - urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; - - return 0; -} - -static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, - gfp_t mem_flags) -{ - int ret; - - ret = alloc_dma_aligned_buffer(urb, mem_flags); - if (ret) - return ret; - - ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); - - /* control packets over dma */ - if (urb->setup_dma) - dma_sync_single_for_device(hcd->self.controller, - urb->setup_dma, sizeof(struct usb_ctrlrequest), - DMA_TO_DEVICE); - - /* urb buffers over dma */ - if (urb->transfer_dma) { - enum dma_data_direction dir; - dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - dma_sync_single_for_device(hcd->self.controller, - urb->transfer_dma, urb->transfer_buffer_length, dir); - } - - if (ret) - free_dma_aligned_buffer(urb); - - return ret; -} - -static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) -{ - struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - - /* Fence read for coherency of AHB master intiated writes */ - if (tegra->phy->instance == 0) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); - else if (tegra->phy->instance == 1) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB2_PREFETCH_ID)); - else if (tegra->phy->instance == 2) - readb(IO_ADDRESS(IO_PPCS_PHYS + USB3_PREFETCH_ID)); - - if (urb->transfer_dma) { - enum dma_data_direction dir; - dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - if (dir == DMA_FROM_DEVICE) - dma_sync_single_for_cpu(hcd->self.controller, - urb->transfer_dma, urb->transfer_buffer_length, - DMA_FROM_DEVICE); - } - - usb_hcd_unmap_urb_for_dma(hcd, urb); - free_dma_aligned_buffer(urb); -} - -void clk_timer_callback(unsigned long data) -{ - struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd*) data; - unsigned long flags; - - if (!timer_pending(&tegra->clk_timer)) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->timer_event = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - schedule_work(&tegra->clk_timer_work); - } -} - -static void clk_timer_work_handler(struct work_struct* clk_timer_work) { - struct tegra_ehci_hcd *tegra = container_of(clk_timer_work, - struct tegra_ehci_hcd, clk_timer_work); - int ret; - unsigned long flags; - bool clock_enabled, timer_event; - - spin_lock_irqsave(&tegra->ehci->lock, flags); - clock_enabled = tegra->clock_enabled; - timer_event = tegra->timer_event; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - if (timer_event) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 0; - tegra->timer_event = 0; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - clk_disable(tegra->emc_clk); - clk_disable(tegra->sclk_clk); - return; - } - - if ((!clock_enabled)) { - ret = mod_timer(&tegra->clk_timer, jiffies + msecs_to_jiffies(2000)); - if (ret) - pr_err("tegra_ehci_urb_enqueue timer modify failed \n"); - clk_enable(tegra->emc_clk); - clk_enable(tegra->sclk_clk); - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - } else { - if (timer_pending(&tegra->clk_timer)) { - mod_timer_pending (&tegra->clk_timer, jiffies - + msecs_to_jiffies(2000)); - } - } -} - -static int tegra_ehci_urb_enqueue ( - struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags) -{ - struct tegra_ehci_hcd *pdata; - int xfertype; - int transfer_buffer_length; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - pdata = dev_get_drvdata(hcd->self.controller); - - xfertype = usb_endpoint_type(&urb->ep->desc); - transfer_buffer_length = urb->transfer_buffer_length; - spin_lock_irqsave(&ehci->lock,flags); - /* Turn on the USB busy hints */ - switch (xfertype) { - case USB_ENDPOINT_XFER_INT: - if (transfer_buffer_length < 255) { - /* Do nothing for interrupt buffers < 255 */ - } else { - /* signal to set the busy hints */ - schedule_work(&pdata->clk_timer_work); - } - break; - case USB_ENDPOINT_XFER_ISOC: - case USB_ENDPOINT_XFER_BULK: - /* signal to set the busy hints */ - schedule_work(&pdata->clk_timer_work); - break; - case USB_ENDPOINT_XFER_CONTROL: - default: - /* Do nothing special here */ - break; - } - spin_unlock_irqrestore(&ehci->lock,flags); - return ehci_urb_enqueue(hcd, urb, mem_flags); -} - static const struct hc_driver tegra_ehci_hc_driver = { .description = hcd_name, .product_desc = "Tegra EHCI Host Controller", @@ -1085,9 +363,10 @@ static const struct hc_driver tegra_ehci_hc_driver = { /* standard ehci functions */ .start = ehci_run, .stop = ehci_stop, + .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, + .endpoint_reset = ehci_endpoint_reset, .get_frame_number = ehci_get_frame, .hub_status_data = ehci_hub_status_data, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, @@ -1101,10 +380,9 @@ static const struct hc_driver tegra_ehci_hc_driver = { .map_urb_for_dma = tegra_ehci_map_urb_for_dma, .unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma, .hub_control = tegra_ehci_hub_control, - .urb_enqueue = tegra_ehci_urb_enqueue, #ifdef CONFIG_PM - .bus_suspend = tegra_ehci_bus_suspend, - .bus_resume = tegra_ehci_bus_resume, + .bus_suspend = tegra_ehci_bus_suspend, + .bus_resume = tegra_ehci_bus_resume, #endif }; @@ -1113,76 +391,30 @@ static int tegra_ehci_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; struct tegra_ehci_hcd *tegra; - struct tegra_ehci_platform_data *pdata; int err = 0; int irq; - int instance = pdev->id; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "Platform data missing\n"); - return -EINVAL; - } tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL); - if (!tegra) + if (!tegra) { + dev_err(&pdev->dev, "memory alloc failed\n"); return -ENOMEM; + } - mutex_init(&tegra->tegra_ehci_hcd_mutex); + mutex_init(&tegra->sync_lock); hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { - dev_err(&pdev->dev, "Unable to create HCD\n"); + dev_err(&pdev->dev, "unable to create HCD\n"); err = -ENOMEM; goto fail_hcd; } platform_set_drvdata(pdev, tegra); - tegra->default_enable = pdata->default_enable; - - tegra->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(tegra->clk)) { - dev_err(&pdev->dev, "Can't get ehci clock\n"); - err = PTR_ERR(tegra->clk); - goto fail_clk; - } - - err = clk_enable(tegra->clk); - if (err) - goto fail_clken; - - - tegra->sclk_clk = clk_get(&pdev->dev, "sclk"); - if (IS_ERR(tegra->sclk_clk)) { - dev_err(&pdev->dev, "Can't get sclk clock\n"); - err = PTR_ERR(tegra->sclk_clk); - goto fail_sclk_clk; - } - - clk_set_rate(tegra->sclk_clk, 80000000); - - tegra->emc_clk = clk_get(&pdev->dev, "emc"); - if (IS_ERR(tegra->emc_clk)) { - dev_err(&pdev->dev, "Can't get emc clock\n"); - err = PTR_ERR(tegra->emc_clk); - goto fail_emc_clk; - } - init_timer(&tegra->clk_timer); - tegra->clk_timer.function = clk_timer_callback; - tegra->clk_timer.data = (unsigned long) tegra; - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - /* Set DDR busy hints to 150MHz. For Tegra 2x SOC, DDR rate is half of EMC rate */ - clk_set_rate(tegra->emc_clk, 300000000); -#else - /* Set DDR busy hints to 100MHz. For Tegra 3x SOC DDR rate equals to EMC rate */ - clk_set_rate(tegra->emc_clk, 100000000); -#endif res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&pdev->dev, "Failed to get I/O memory\n"); + dev_err(&pdev->dev, "failed to get I/O memory\n"); err = -ENXIO; goto fail_io; } @@ -1190,179 +422,103 @@ static int tegra_ehci_probe(struct platform_device *pdev) hcd->rsrc_len = resource_size(res); hcd->regs = ioremap(res->start, resource_size(res)); if (!hcd->regs) { - dev_err(&pdev->dev, "Failed to remap I/O memory\n"); + dev_err(&pdev->dev, "failed to remap I/O memory\n"); err = -ENOMEM; goto fail_io; } - INIT_WORK(&tegra->clk_timer_work, clk_timer_work_handler); + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + err = -ENODEV; + goto fail_irq; + } + set_irq_flags(irq, IRQF_VALID); + tegra->irq = irq; - tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config, - TEGRA_USB_PHY_MODE_HOST, pdata->phy_type); + tegra->phy = tegra_usb_phy_open(pdev); if (IS_ERR(tegra->phy)) { - dev_err(&pdev->dev, "Failed to open USB phy\n"); + dev_err(&pdev->dev, "failed to open USB phy\n"); err = -ENXIO; - goto fail_phy; + goto fail_irq; } - tegra->phy->hotplug = pdata->hotplug; - err = tegra_usb_phy_power_on(tegra->phy, true); + err = tegra_usb_phy_power_on(tegra->phy); if (err) { - dev_err(&pdev->dev, "Failed to power on the phy\n"); - goto fail; - } - - tegra->host_resumed = 1; - tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend; - tegra->ehci = hcd_to_ehci(hcd); - - irq = platform_get_irq(pdev, 0); - if (!irq) { - dev_err(&pdev->dev, "Failed to get IRQ\n"); - err = -ENODEV; - goto fail; + dev_err(&pdev->dev, "failed to power on the phy\n"); + goto fail_phy; } - set_irq_flags(irq, IRQF_VALID); - tegra->irq = irq; -#ifdef CONFIG_USB_OTG_UTILS - if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = otg_get_transceiver(); - if (tegra->transceiver) - otg_set_host(tegra->transceiver, &hcd->self); + err = tegra_usb_phy_init(tegra->phy); + if (err) { + dev_err(&pdev->dev, "failed to init the phy\n"); + goto fail_phy; } -#endif err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (err) { - dev_err(&pdev->dev, "Failed to add USB HCD error = %d\n", err); - goto fail; + dev_err(&pdev->dev, "Failed to add USB HCD, error=%d\n", err); + goto fail_phy; } err = enable_irq_wake(tegra->irq); if (err < 0) { dev_warn(&pdev->dev, - "Couldn't enable USB host mode wakeup, irq=%d, " - "error=%d\n", tegra->irq, err); + "Couldn't enable USB host mode wakeup, irq=%d, " + "error=%d\n", irq, err); err = 0; tegra->irq = 0; } - return err; + tegra->ehci = hcd_to_ehci(hcd); -fail: #ifdef CONFIG_USB_OTG_UTILS - if (tegra->transceiver) { - otg_set_host(tegra->transceiver, NULL); - otg_put_transceiver(tegra->transceiver); + if (tegra_usb_phy_otg_supported(tegra->phy)) { + tegra->transceiver = otg_get_transceiver(); + if (tegra->transceiver) + otg_set_host(tegra->transceiver, &hcd->self); } #endif - tegra_usb_phy_close(tegra->phy); + return err; + fail_phy: + tegra_usb_phy_close(tegra->phy); +fail_irq: iounmap(hcd->regs); fail_io: - clk_disable(tegra->emc_clk); - clk_put(tegra->emc_clk); -fail_emc_clk: - clk_disable(tegra->sclk_clk); - clk_put(tegra->sclk_clk); -fail_sclk_clk: - clk_disable(tegra->clk); -fail_clken: - clk_put(tegra->clk); -fail_clk: usb_put_hcd(hcd); fail_hcd: kfree(tegra); + return err; } + #ifdef CONFIG_PM static int tegra_ehci_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - if (tegra->default_enable) - clk_enable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - if (tegra->default_enable) - clk_enable(tegra->clk); - - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; + return tegra_usb_phy_power_on(tegra->phy); } -static int tegra_ehci_resume(struct device *dev) +static int tegra_ehci_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - int ret; - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - ret = tegra_usb_resume(hcd, true); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ret; -} - -static int tegra_ehci_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); - struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); - int ret; - u32 val; - - mutex_lock(&tegra->tegra_ehci_hcd_mutex); - /* if bus suspend is failed means there is remote wakeup resume, - then abort the PM suspend */ - if (tegra->bus_suspended_fail) { - tegra->bus_suspended_fail = false; - pr_err("%s: bus suspend failed, aborting driver suspend\n", __func__); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + /* bus suspend could have failed because of remote wakeup resume */ + if (tegra->bus_suspended_fail) return -EBUSY; - } - if (tegra->phy->hotplug) { - /* Disable PHY clock valid interrupts while going into suspend*/ - val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); - val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB; - writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); - } - - if ((tegra->bus_suspended) && (tegra->power_down_on_bus_suspend)) { - if (tegra->default_enable) - clk_disable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return 0; - } - - if (time_before(jiffies, tegra->ehci->next_statechange)) - msleep(10); - - ret = tegra_usb_suspend(hcd, true); - if (tegra->default_enable) - clk_disable(tegra->clk); - mutex_unlock(&tegra->tegra_ehci_hcd_mutex); - return ret; + else + return tegra_usb_phy_power_off(tegra->phy); } static struct dev_pm_ops tegra_ehci_dev_pm_ops = { - .suspend = tegra_ehci_suspend, - .resume = tegra_ehci_resume, - .resume_noirq = tegra_ehci_resume_noirq, + .suspend_noirq = tegra_ehci_suspend_noirq, + .resume_noirq = tegra_ehci_resume_noirq, }; - #endif static int tegra_ehci_remove(struct platform_device *pdev) @@ -1372,9 +528,6 @@ static int tegra_ehci_remove(struct platform_device *pdev) if (tegra == NULL || hcd == NULL) return -EINVAL; - /* make sure controller is on as we will touch its registers */ - if (!tegra->host_resumed) - tegra_ehci_power_up(hcd, true); #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { @@ -1383,30 +536,15 @@ static int tegra_ehci_remove(struct platform_device *pdev) } #endif - /* Turn Off Interrupts */ - ehci_writel(tegra->ehci, 0, &tegra->ehci->regs->intr_enable); - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (tegra->irq) disable_irq_wake(tegra->irq); usb_remove_hcd(hcd); usb_put_hcd(hcd); - tegra_usb_phy_power_off(tegra->phy, true); + tegra_usb_phy_power_off(tegra->phy); tegra_usb_phy_close(tegra->phy); iounmap(hcd->regs); - - del_timer_sync(&tegra->clk_timer); - - clk_disable(tegra->clk); - clk_put(tegra->clk); - - if (tegra->clock_enabled) { - clk_disable(tegra->sclk_clk); - clk_disable(tegra->emc_clk); - } - clk_put(tegra->sclk_clk); - clk_put(tegra->emc_clk); - kfree(tegra); + return 0; } @@ -1421,12 +559,12 @@ static void tegra_ehci_hcd_shutdown(struct platform_device *pdev) static struct platform_driver tegra_ehci_driver = { .probe = tegra_ehci_probe, - .remove = tegra_ehci_remove, + .remove = tegra_ehci_remove, .shutdown = tegra_ehci_hcd_shutdown, - .driver = { - .name = "tegra-ehci", + .driver = { + .name = driver_name, #ifdef CONFIG_PM - .pm = &tegra_ehci_dev_pm_ops, + .pm = &tegra_ehci_dev_pm_ops, #endif } }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 509934ceb4a9..cfbdf32ec0b2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -143,6 +143,7 @@ struct ehci_hcd { /* one per controller */ #ifdef CONFIG_USB_EHCI_TEGRA unsigned controller_resets_phy:1; unsigned controller_remote_wakeup:1; + unsigned broken_hostpc_phcd:1; #endif /* required for usb32 quirk */ |