summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/nvddk/nvddk_usbphy.c16
-rwxr-xr-xdrivers/usb/host/ehci-tegra.c69
-rwxr-xr-xinclude/linux/tegra_devices.h1
3 files changed, 80 insertions, 6 deletions
diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
index 7a9da42cdf7c..2ec63f3eae91 100644
--- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
+++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
@@ -404,8 +404,11 @@ NvDdkUsbPhyHelperThread(
/* wait for the signal to turn on/off the busy hints and vbus */
NvOsSemaphoreWaitTimeout(hUsbPhy->HelperThreadSema, NV_WAIT_INFINITE);
- /* Turn on/off the USB busy hints */
- UsbPhyDfsBusyHint(hUsbPhy, hUsbPhy->IsPhyPoweredUp, NV_WAIT_INFINITE);
+ if (!(hUsbPhy->IsPhyPoweredUp && hUsbPhy->IsHostMode))
+ {
+ /* Turn on/off the USB busy hints */
+ UsbPhyDfsBusyHint(hUsbPhy, hUsbPhy->IsPhyPoweredUp, NV_WAIT_INFINITE);
+ }
/* Turn on/off the vbus for host mode */
if (hUsbPhy->IsHostMode)
{
@@ -557,9 +560,12 @@ NvDdkUsbPhyOpen(
// Open the H/W interface
UsbPhyOpenHwInterface(pUsbPhy);
- /* enable the busy hints for USB */
- UsbPhyDfsBusyHint(pUsbPhy, NV_TRUE, NV_WAIT_INFINITE);
-
+ /* Enable the busy hints for USB device mode, for host mode *
+ * transaction based busy hints are on/off mechanism is present */
+ if (pUsbPhy->pProperty->UsbMode != NvOdmUsbModeType_Host)
+ {
+ UsbPhyDfsBusyHint(pUsbPhy, NV_TRUE, NV_WAIT_INFINITE);
+ }
// Initialize the USB Phy
NV_CHECK_ERROR_CLEANUP(UsbPhyInitialize(pUsbPhy));
}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 2784aa641e78..5c69667bd6ca 100755
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -45,6 +45,33 @@
#define TEGRA_USB_USBMODE_REG_OFFSET (0x1a8)
#define TEGRA_USB_USBMODE_HOST (3)
+/*
+ * Work thread function for setting the usb busy hints.
+ *
+ * This work thread is created to avoid the pre-emption from the ISR context.
+ * Busy hints are controlled based on the USB transcations on the bus .
+ * Busy hints function cannot be called from ISR as NvRmPowerBusyHintMulti()
+ * 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);
+}
+
static void tegra_ehci_power_up(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -357,6 +384,44 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd)
}
#endif
+
+static int tegra_ehci_urb_enqueue(
+ 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);
+}
+
static const struct hc_driver tegra_ehci_hc_driver = {
.description = hcd_name,
.product_desc = "Tegra Ehci host controller",
@@ -370,7 +435,7 @@ static const struct hc_driver tegra_ehci_hc_driver = {
.start = ehci_run,
.stop = ehci_stop,
.shutdown = tegra_ehci_shutdown,
- .urb_enqueue = ehci_urb_enqueue,
+ .urb_enqueue = tegra_ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.get_frame_number = ehci_get_frame,
@@ -433,6 +498,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
}
+ INIT_WORK(&pdata->work, tegra_ehci_busy_hint_work);
+
/* Init the tegra USB phy */
e = tegra_ehci_reinit(hcd);
if (e) {
diff --git a/include/linux/tegra_devices.h b/include/linux/tegra_devices.h
index 4f1d4c8fb267..07fbc84c44c7 100755
--- a/include/linux/tegra_devices.h
+++ b/include/linux/tegra_devices.h
@@ -50,6 +50,7 @@ struct tegra_hcd_platform_data {
*/
NvU32 phyPowerRail;
NvDdkUsbPhyHandle hUsbPhy;
+ struct work_struct work;
};
/* Platform data for USB OTG driver */