diff options
-rw-r--r-- | arch/arm/mach-tegra/nvddk/nvddk_usbphy.c | 16 | ||||
-rwxr-xr-x | drivers/usb/host/ehci-tegra.c | 69 | ||||
-rwxr-xr-x | include/linux/tegra_devices.h | 1 |
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 */ |