summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsgadagottu <sgadagottu@nvidia.com>2010-03-20 01:57:00 +0530
committerGary King <gking@nvidia.com>2010-03-21 09:33:01 -0800
commit873a30edbac4017af82525c1e2385d2b743f436e (patch)
tree4d8f39403a5277a2ab054d44034cf10415640017
parent3fd0b4e9712b848d23f2f341ab4e5b472f50932f (diff)
tegra usb: Switching of USB busy hints, when no active USB tranferstegra-9.12.8
are happening though a device connection on host port. Currently for harmony, an intergrated SMSC hub present on USB3. Because of this device connection is present on USB3 and USB power busy hints are always on. To avoid this, for host by default busy hints are off and USB busy hints will be on for : 1.Bulk and isochronus transfers 2.Interrupts transfers with buffer length >= 256. Busy hint will be on for pre-defined amount of time and after that busy hints will be off automatically. With this in idle state power is reduced. Tested on : Harmony Enumeartion is happening fine. LAN and USB HID devices are working fine Change-Id: Ifd653bebfb52c7702f7d24bf11ccf93e62ea0f66 Reviewed-on: http://git-master/r/915 Reviewed-by: Gary King <gking@nvidia.com> Tested-by: Gary King <gking@nvidia.com>
-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 */