summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ehci-tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ehci-tegra.c')
-rwxr-xr-xdrivers/usb/host/ehci-tegra.c69
1 files changed, 68 insertions, 1 deletions
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) {