summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorVenkat Moganty <vmoganty@nvidia.com>2010-04-29 20:44:29 +0530
committerYu-Huan Hsu <yhsu@nvidia.com>2010-05-02 17:58:43 -0700
commit1b8406f6f9556f8d205ebf01035350e587c11764 (patch)
tree4086f6d63860feb729938e6e44d79973981e632c /drivers
parent7eb7a42ddadfe5dbcb529e7d0e908dbb0c1c2b18 (diff)
tegra usb:Modifications to usb power up/down sequence
Removed helper thread and replaced it with Worker Queues in udc and ehci drivers to handle usbphy power up/down sequence. Made changes to turn off usb power rail based on vbus detection mechanism is selected as PMU. Fixed usb host LP0 exit sequence. Bug 667912: AVDD_USB_Power is consuming 3.82mW of power in OSIdle and ULP audio playback case. Change-Id: I3a77d0ecb4f0b81dafe705100451c42641f0bfb9 Reviewed-on: http://git-master/r/1221 Tested-by: Hanumanth Venkateswa Moganty <vmoganty@nvidia.com> Tested-by: Dara Ramesh <dramesh@nvidia.com> Reviewed-by: Seshendra Gadagottu <sgadagottu@nvidia.com> Tested-by: Seshendra Gadagottu <sgadagottu@nvidia.com> Reviewed-by: Narendra Damahe <ndamahe@nvidia.com> Tested-by: Narendra Damahe <ndamahe@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'drivers')
-rwxr-xr-xdrivers/usb/gadget/fsl_udc_core.c113
-rwxr-xr-xdrivers/usb/gadget/fsl_usb2_udc.h3
-rwxr-xr-xdrivers/usb/host/ehci-tegra.c296
-rw-r--r--drivers/usb/host/ehci.h1
-rwxr-xr-xdrivers/usb/otg/tegra-otg.c8
5 files changed, 308 insertions, 113 deletions
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 199034307a37..cbf6313585c3 100755
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -74,8 +74,8 @@ static struct usb_sys_interface *usb_sys_regs;
/* Charger current limit=1800mA, as per the USB charger spec */
#define USB_CHARGING_CURRENT_LIMIT_MA 1800
-/* 100msec wait time for charger detection after vbus is detected */
-#define USB_CHARGER_DETECTION_WAIT_TIME_MS 100
+/* 1 sec wait time for charger detection after vbus is detected */
+#define USB_CHARGER_DETECTION_WAIT_TIME_MS 1000
/* it is initialized in probe() */
static struct fsl_udc *udc_controller = NULL;
@@ -1830,22 +1830,21 @@ static void fsl_udc_restart(struct fsl_udc *udc)
}
#endif
+
/*
- * USB device controller interrupt handler
+ * Work thread function for handling the USB power sequence.
+ *
+ * This work thread is created to avoid the pre-emption from the ISR context.
+ * USB Power Rail is controlled based on the USB cable connection.
+ * USB Power rail function cannot be called from ISR as NvRmPmuSetVoltage()
+ * uses I2C driver, that waits on semaphore during the I2C transaction
+ * this will cause the pre-emption if called in ISR.
*/
-static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+static void fsl_udc_irq_work(struct work_struct* irq_work)
{
- struct fsl_udc *udc = _udc;
- u32 irq_src;
- irqreturn_t status = IRQ_NONE;
- unsigned long flags;
-#if defined(CONFIG_ARCH_TEGRA)
+ struct fsl_udc *udc = container_of (irq_work, struct fsl_udc, irq_work);
u32 temp;
-#endif
-
-#if defined(CONFIG_ARCH_TEGRA)
- spin_lock_irqsave(&udc->lock, flags);
/* check OTG tranceiver is available or not */
if (udc->transceiver) {
if (udc->transceiver->state == OTG_STATE_B_PERIPHERAL) {
@@ -1863,8 +1862,10 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
if (udc->vbus_active) {
/* If cable disconnected, cancel any delayed work */
cancel_delayed_work(&udc->work);
+ spin_lock(&udc->lock);
/* Reset all internal Queues and inform client driver */
reset_queues(udc);
+ spin_unlock(&udc->lock);
/* stop the controller and turn off the clocks */
dr_controller_stop(udc);
platform_udc_clk_suspend();
@@ -1876,17 +1877,12 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
regulator_set_current_limit(udc->vbus_regulator, 0, 0);
}
}
- } else {
- spin_unlock_irqrestore(&udc->lock, flags);
- return IRQ_HANDLED;
}
}else {
/* VBUS A session detection interrupts. When the interrupt is received,
* mark the vbus active shadow.
*/
temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
- /* write back the register to clear the interrupt */
- fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) {
if (temp & USB_SYS_VBUS_STATUS) {
udc->vbus_active = 1;
@@ -1899,7 +1895,9 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
} else {
/* If cable disconnected, cancel any delayed work */
cancel_delayed_work(&udc->work);
+ spin_lock(&udc->lock);
reset_queues(udc);
+ spin_unlock(&udc->lock);
udc->vbus_active = 0;
udc->usb_state = USB_STATE_DEFAULT;
platform_udc_clk_suspend();
@@ -1909,9 +1907,55 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
}
/* printk("USB cable dis-connected\n"); */
}
- status = IRQ_HANDLED;
}
}
+}
+
+/*
+ * USB device controller interrupt handler
+ */
+static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+{
+ struct fsl_udc *udc = _udc;
+ u32 irq_src;
+ irqreturn_t status = IRQ_NONE;
+ unsigned long flags;
+#if defined(CONFIG_ARCH_TEGRA)
+ u32 temp;
+#endif
+
+
+#if defined(CONFIG_ARCH_TEGRA)
+ spin_lock_irqsave(&udc->lock, flags);
+ /* check OTG tranceiver is available or not */
+ if (udc->transceiver) {
+ if (udc->transceiver->state == OTG_STATE_B_PERIPHERAL) {
+ if (!udc->vbus_active) {
+ schedule_work(&udc->irq_work);
+ }
+ } else if (udc->transceiver->state == OTG_STATE_A_SUSPEND) {
+ if (udc->vbus_active) {
+ schedule_work(&udc->irq_work);
+ } else {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return IRQ_HANDLED;
+ }
+ } else {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return IRQ_HANDLED;
+ }
+ }else {
+ /* VBUS A session detection interrupts. When the interrupt is received,
+ * mark the vbus active shadow.
+ */
+ temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
+ /* write back the register to clear the interrupt */
+ fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
+ if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) {
+ schedule_work(&udc->irq_work);
+ }
+ status = IRQ_HANDLED;
+ }
spin_unlock_irqrestore(&udc->lock, flags);
#endif
@@ -2648,6 +2692,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
/* create a delayed work for detecting the USB charger */
INIT_DELAYED_WORK(&udc_controller->work, fsl_udc_charger_detection);
+ /* create a work for controlling the clocks to the Phy */
+ INIT_WORK(&udc_controller->irq_work, fsl_udc_irq_work);
+
#if defined(CONFIG_ARCH_TEGRA)
#ifdef CONFIG_USB_OTG_UTILS
/* Get the OTG transceiver. If OTG is enabled then transceiver
@@ -2737,8 +2784,20 @@ static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
if (udc_controller->transceiver->state != OTG_STATE_B_PERIPHERAL) {
/* we are not in device mode, return */
return 0;
+ } else {
+ udc_controller->transceiver->state = OTG_STATE_UNDEFINED;
}
}
+ if (udc_controller->vbus_active)
+ {
+ spin_lock(&udc_controller->lock);
+ /* Reset all internal Queues and inform client driver */
+ reset_queues(udc_controller);
+ udc_controller->vbus_active = 0;
+ udc_controller->usb_state = USB_STATE_DEFAULT;
+ spin_unlock(&udc_controller->lock);
+ }
+ /* stop the controller and turn off the clocks */
dr_controller_stop(udc_controller);
platform_udc_clk_suspend();
return 0;
@@ -2751,12 +2810,22 @@ static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
static int fsl_udc_resume(struct platform_device *pdev)
{
if (udc_controller->transceiver) {
- if (udc_controller->transceiver->state != OTG_STATE_B_PERIPHERAL) {
- /* we are not in device mode, return */
+ if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_ID_PIN_STATUS)) {
+ /* If ID status is low means host is connected, return */
return 0;
}
+ /* enable clock and check for VBUS */
+ platform_udc_clk_resume();
+ if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS)) {
+ /* if there is no VBUS then power down the clocks and return */
+ platform_udc_clk_suspend();
+ return 0;
+ }
+ } else {
+ /* enable the clocks to the controller */
+ platform_udc_clk_resume();
}
- platform_udc_clk_resume();
+
#if defined(CONFIG_ARCH_TEGRA)
fsl_udc_restart(udc_controller);
#else
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
index d2225d89ad66..5cdd6e983c8c 100755
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -447,6 +447,8 @@ struct ep_td_struct {
#define USB_SYS_VBUS_WAKEUP_INT_ENABLE 0x100
#define USB_SYS_VBUS_WAKEUP_INT_STATUS 0x200
#define USB_SYS_VBUS_STATUS 0x400
+#define USB_SYS_ID_PIN_STATUS (0x4)
+
/*-------------------------------------------------------------------------*/
@@ -515,6 +517,7 @@ struct fsl_udc {
u8 device_address; /* Device USB address */
struct regulator *vbus_regulator; /* regulator for drawing VBUS */
struct delayed_work work; /* delayed work for charger detection */
+ struct work_struct irq_work; /* irq work for controling the usb power*/
};
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 5c69667bd6ca..53301cb4342e 100755
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -54,22 +54,20 @@
* uses vfree and vmalloc functions which are not supposed to call from the
* ISR context
*/
-
static void tegra_ehci_busy_hint_work(struct work_struct* work)
{
- struct tegra_hcd_platform_data *pdata =
- container_of(work, struct tegra_hcd_platform_data, work);
- NvDdkUsbPhyIoctl_UsbBusyHintsOnOffInputArgs busyhint;
- busyhint.OnOff = NV_TRUE;
-
- /* USB transfers will be done with in 1 sec, this need to be *
- * fine tuned (if required). with safe limit set to 2 sec */
- busyhint.BoostDurationMs = 2000;
- NvDdkUsbPhyIoctl(
- pdata->hUsbPhy,
- NvDdkUsbPhyIoctlType_UsbBusyHintsOnOff,
- &busyhint,
- NULL);
+ struct tegra_hcd_platform_data *pdata =
+ container_of(work, struct tegra_hcd_platform_data, work);
+ NvDdkUsbPhyIoctl_UsbBusyHintsOnOffInputArgs busyhint;
+ busyhint.OnOff = NV_TRUE;
+
+ /* USB transfers will be done with in 1 sec, this need to be *
+ * fine tuned (if required). with safe limit set to 2 sec */
+ busyhint.BoostDurationMs = 2000;
+ NvDdkUsbPhyIoctl(pdata->hUsbPhy,
+ NvDdkUsbPhyIoctlType_UsbBusyHintsOnOff,
+ &busyhint,
+ NULL);
}
static void tegra_ehci_power_up(struct usb_hcd *hcd)
@@ -157,7 +155,6 @@ static int tegra_ehci_hub_control (
return retval;
}
-#ifdef CONFIG_USB_OTG_UTILS
static void tegra_ehci_restart (struct usb_hcd *hcd)
{
unsigned int temp;
@@ -190,7 +187,6 @@ static void tegra_ehci_restart (struct usb_hcd *hcd)
/* Turn On Interrupts */
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
}
-#endif
static void tegra_ehci_shutdown (struct usb_hcd *hcd)
{
@@ -203,6 +199,53 @@ static void tegra_ehci_shutdown (struct usb_hcd *hcd)
tegra_ehci_power_down(hcd);
}
+/*
+ * Work thread function for handling the USB power sequence.
+ *
+ * This work thread is created to avoid the pre-emption from the ISR context.
+ * USB Power Rail and Vbus are controlled based on the USB cable connection.
+ * USB Power rail function and VBUS control function cannot be called from ISR
+ * as NvRmPmuSetVoltage() uses I2C driver, that waits on semaphore
+ * during the I2C transaction this will cause the pre-emption if called in ISR.
+ */
+static void tegra_ehci_irq_work(struct work_struct* irq_work)
+{
+ struct ehci_hcd *ehci = container_of(irq_work, struct ehci_hcd, irq_work);
+ struct usb_hcd *hcd = ehci_to_hcd(ehci);
+ struct tegra_hcd_platform_data *pdata;
+ u32 status;
+
+ pdata = hcd->self.controller->platform_data;
+
+#ifdef CONFIG_USB_OTG_UTILS
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG)
+ && ehci->transceiver) {
+ if (ehci->transceiver->state == OTG_STATE_A_HOST) {
+ if (!ehci->host_reinited) {
+ ehci->host_reinited = 1;
+ tegra_ehci_power_up(hcd);
+ tegra_ehci_restart(hcd);
+ }
+ }
+ } else
+#endif
+ {
+ if (pdata->pUsbProperty->IdPinDetectionType ==
+ NvOdmUsbIdPinType_CableId) {
+ /* read otgsc register for ID pin status change */
+ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
+ /* Check pin status and enable/disable the power */
+ if (status & TEGRA_USB_ID_PIN_STATUS) {
+ tegra_ehci_power_down(hcd);
+ } else {
+ tegra_ehci_power_up(hcd);
+ }
+ }
+ }
+}
+
+
+
static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
@@ -218,9 +261,7 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
&& ehci->transceiver) {
if (ehci->transceiver->state == OTG_STATE_A_HOST) {
if (!ehci->host_reinited) {
- ehci->host_reinited = 1;
- tegra_ehci_power_up(hcd);
- tegra_ehci_restart(hcd);
+ schedule_work(&ehci->irq_work);
}
} else if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) {
if (!ehci->host_reinited) {
@@ -242,12 +283,7 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
/* Check if there is any ID pin interrupt */
if (status & TEGRA_USB_ID_INT_STATUS) {
- /* Check pin status and enable/disable the power */
- if (status & TEGRA_USB_ID_PIN_STATUS) {
- tegra_ehci_power_down(hcd);
- } else {
- tegra_ehci_power_up(hcd);
- }
+ schedule_work(&ehci->irq_work);
}
}
}
@@ -317,40 +353,45 @@ static int tegra_ehci_setup(struct usb_hcd *hcd)
return retval;
}
-#if defined(CONFIG_PM)
+
static int tegra_ehci_bus_suspend(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- int err_status = 0;
-
-#ifdef CONFIG_USB_OTG_UTILS
struct tegra_hcd_platform_data *pdata;
+
/* initialize the platform data pointer */
pdata = hcd->self.controller->platform_data;
+
+#ifdef CONFIG_USB_OTG_UTILS
if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG)
&& ehci->transceiver) {
if (ehci->transceiver->state != OTG_STATE_A_HOST) {
/* we are not in host mode, return */
- return err_status;
+ return 0;
}
}
#endif
- if (ehci->host_resumed) {
- err_status = ehci_bus_suspend(hcd);
- if (!err_status)
- tegra_ehci_power_down(hcd);
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_Host)
+ && (pdata->pUsbProperty->IdPinDetectionType ==
+ NvOdmUsbIdPinType_CableId)) {
+ u32 status;
+ /* check for ID pin status */
+ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
+ /* If Id pin is high means host is not connected return */
+ if (status & TEGRA_USB_ID_PIN_STATUS) {
+ return 0;
+ }
}
- return err_status;
+
+ return ehci_bus_suspend(hcd);
}
static int tegra_ehci_bus_resume(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- int err_status = 0;
- u32 status;
-
struct tegra_hcd_platform_data *pdata;
+
/* initialize the platform data pointer */
pdata = hcd->self.controller->platform_data;
@@ -359,67 +400,61 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd)
&& ehci->transceiver) {
if (ehci->transceiver->state != OTG_STATE_A_HOST) {
/* we are not in host mode, return */
- return err_status;
+ return 0;
}
}
#endif
- if (!ehci->host_resumed) {
- tegra_ehci_power_up(hcd);
- err_status = ehci_bus_resume(hcd);
- }
-
- if ((pdata->pUsbProperty->UsbMode != NvOdmUsbModeType_OTG)
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_Host)
&& (pdata->pUsbProperty->IdPinDetectionType ==
NvOdmUsbIdPinType_CableId)) {
- /* read otgsc register for ID pin status */
+ u32 status;
+ /* read ID pin status */
status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
- writel(status, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET));
- /* If no Id pin then disable the power */
+ /* If Id pin is high no host then return */
if (status & TEGRA_USB_ID_PIN_STATUS) {
- tegra_ehci_power_down(hcd);
+ return 0;
}
- }
- return err_status;
-}
-#endif
+ }
+ return ehci_bus_resume(hcd);
+}
static int tegra_ehci_urb_enqueue(
- struct usb_hcd *hcd,
- struct urb *urb,
- gfp_t mem_flags
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags
) {
- struct tegra_hcd_platform_data *pdata;
- int xfertype;
- int transfer_buffer_length;
-
- pdata = hcd->self.controller->platform_data;
-
- xfertype = usb_endpoint_type(&urb->ep->desc);
- transfer_buffer_length = urb->transfer_buffer_length;
- /* 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->work);
- }
- break;
- case USB_ENDPOINT_XFER_ISOC:
- case USB_ENDPOINT_XFER_BULK:
- // signal to set the busy hints
- schedule_work(&pdata->work);
- break;
- case USB_ENDPOINT_XFER_CONTROL:
- default:
- /* Do nothing special here */
- break;
- }
-
- return ehci_urb_enqueue(hcd, urb, mem_flags);
+ struct tegra_hcd_platform_data *pdata;
+ int xfertype;
+ int transfer_buffer_length;
+
+ pdata = hcd->self.controller->platform_data;
+
+ xfertype = usb_endpoint_type(&urb->ep->desc);
+ transfer_buffer_length = urb->transfer_buffer_length;
+ /* 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->work);
+ }
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ case USB_ENDPOINT_XFER_BULK:
+ // signal to set the busy hints
+ schedule_work(&pdata->work);
+ break;
+ case USB_ENDPOINT_XFER_CONTROL:
+ default:
+ /* Do nothing special here */
+ break;
+ }
+
+ return ehci_urb_enqueue(hcd, urb, mem_flags);
}
static const struct hc_driver tegra_ehci_hc_driver = {
@@ -458,6 +493,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
int irq;
unsigned int temp;
static u64 dummy_mask = DMA_32BIT_MASK;
+ struct ehci_hcd *ehci;
pdata = (struct tegra_hcd_platform_data *)pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "Cannot run without platform data\n");
@@ -531,6 +567,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
set_irq_flags(irq, IRQF_VALID);
+ ehci = hcd_to_ehci(hcd);
+ INIT_WORK(&ehci->irq_work, tegra_ehci_irq_work);
+
e = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
if (e != 0)
goto fail;
@@ -538,7 +577,6 @@ static int tegra_ehci_probe(struct platform_device *pdev)
#ifdef CONFIG_USB_OTG_UTILS
if (pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) {
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
ehci->transceiver = otg_get_transceiver();
if (ehci->transceiver) {
otg_set_host(ehci->transceiver, (struct usb_bus *)hcd);
@@ -587,6 +625,86 @@ fail:
return e;
}
+#if defined(CONFIG_PM)
+static int tegra_ehci_resume(struct platform_device * pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct tegra_hcd_platform_data *pdata;
+ u32 status;
+
+ /* initialize the platform data pointer */
+ pdata = hcd->self.controller->platform_data;
+
+ /* read otgsc register for ID pin status */
+ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
+
+#ifdef CONFIG_USB_OTG_UTILS
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG)
+ && ehci->transceiver) {
+ /* check if ID pin is high then no host return */
+ if (status & TEGRA_USB_ID_PIN_STATUS) {
+ return 0;
+ }
+ else {
+ /* set HCD flags to start host ISR */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ ehci->host_reinited = 1;
+ ehci->transceiver->state = OTG_STATE_A_HOST;
+ }
+ }
+#endif
+
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_Host)
+ && (pdata->pUsbProperty->IdPinDetectionType ==
+ NvOdmUsbIdPinType_CableId)) {
+ /* If no Id pin then return */
+ if (status & TEGRA_USB_ID_PIN_STATUS) {
+ return 0;
+ }
+ }
+
+ if (!ehci->host_resumed) {
+ tegra_ehci_power_up(hcd);
+ tegra_ehci_restart(hcd);
+ }
+
+ return 0;
+}
+
+static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+#ifdef CONFIG_USB_OTG_UTILS
+ struct tegra_hcd_platform_data *pdata;
+ /* initialize the platform data pointer */
+ pdata = hcd->self.controller->platform_data;
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG)
+ && ehci->transceiver) {
+ if (ehci->transceiver->state != OTG_STATE_A_HOST) {
+ /* we are not in host mode, return */
+ return 0;
+ }
+ else {
+ ehci->transceiver->state = OTG_STATE_UNDEFINED;
+ ehci->host_reinited = 0;
+ ehci_halt(ehci);
+ /* indicate hcd flags, that hardware is not accessable now */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ }
+ }
+#endif
+
+ if (ehci->host_resumed) {
+ tegra_ehci_power_down(hcd);
+ }
+
+ return 0;
+}
+#endif
+
static int tegra_ehci_remove(struct platform_device *pdev)
{
struct tegra_hcd_platform_data *pdata = pdev->dev.platform_data;
@@ -612,6 +730,10 @@ static struct platform_driver tegra_ehci_driver =
{
.probe = tegra_ehci_probe,
.remove = tegra_ehci_remove,
+#if defined(CONFIG_PM)
+ .suspend = tegra_ehci_suspend,
+ .resume = tegra_ehci_resume,
+#endif
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "tegra-ehci",
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 1e4394d4f363..ed2ec5b5e47d 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -135,6 +135,7 @@ struct ehci_hcd { /* one per controller */
unsigned controller_resets_phy:1;
unsigned host_reinited:1; /* tegra */
unsigned host_resumed:1; /* tegra */
+ struct work_struct irq_work; /* tegra irq work for power control*/
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c
index a17d8d90f44f..1f300308fb78 100755
--- a/drivers/usb/otg/tegra-otg.c
+++ b/drivers/usb/otg/tegra-otg.c
@@ -111,16 +111,16 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
temp &= ~TEGRA_USB_VBUS_INT_STATUS;
writel(temp, (sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET));
- spin_lock_irqsave(&sg_tegra_otg->lock, flags);
/* Check if we detect any device connected */
if (!(temp & TEGRA_USB_ID_STATUS)) {
struct usb_hcd *hcd = (struct usb_hcd *)otg->host;
+ spin_lock_irqsave(&sg_tegra_otg->lock, flags);
otg->state = OTG_STATE_A_HOST;
/* set HCD flags to start host ISR */
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ spin_unlock_irqrestore(&sg_tegra_otg->lock, flags);
NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0);
}
- spin_unlock_irqrestore(&sg_tegra_otg->lock, flags);
return 0;
}
@@ -179,7 +179,7 @@ static int __init tegra_otg_probe(struct platform_device *pdev)
NvDdkUsbPhyOpen(s_hRmGlobal, instance, &sg_tegra_otg->usb_phy)
);
NV_CHECK_ERROR_CLEANUP(
- NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0)
+ NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_FALSE, 0)
);
sg_tegra_otg->instance = pdev->id;
sg_tegra_otg->dev = &pdev->dev;
@@ -224,7 +224,7 @@ static int __init tegra_otg_probe(struct platform_device *pdev)
goto err_otg;
}
- NvDdkUsbPhyPowerDown(sg_tegra_otg->usb_phy, NV_TRUE, 0);
+ NvDdkUsbPhyPowerDown(sg_tegra_otg->usb_phy, NV_FALSE, 0);
return 0;