summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
authorVenkat Moganty <vmoganty@nvidia.com>2010-01-15 19:56:46 +0530
committerVenkat Moganty <vmoganty@nvidia.com>2010-01-19 11:24:37 +0530
commit98968646e2faada52420581bfcf5f80362f94f69 (patch)
treeca199f73fa08e29726fc53b0050fb253d1b8e6c3 /arch/arm
parent9d42c3949099b2310182e97b3ea23e641dc82286 (diff)
TEGRA - Adding USB OTG feature
Enabling support for the OTG in NVIDIA Tegra SoCs by providing simple transceiver interface for detecting the Host or Device based on the USBID and VBUS sensors. Bug 629098 USB OTG problem Tested on Whistler with Android USB OTG is tested on whistler USB port1, by setting the USB mode type as "NvOdmUsbModeType_OTG" in the ODM usb property of arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c Tested OTG-HOST by connecting USB keyboard and OTG-device by connecting adb. Change-Id: I860e1f4be5f7c96765c2ce1ca320fdf212164811
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/configs/tegra_whistler_android_defconfig4
-rw-r--r--arch/arm/mach-tegra/include/mach/platform.h1
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-tegra/include/nvddk_usbphy.h6
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-tegra/init_common.c71
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-tegra/nvddk/nvddk_usbphy.c65
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h8
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-tegra/tegra_sysmap.c14
7 files changed, 159 insertions, 10 deletions
diff --git a/arch/arm/configs/tegra_whistler_android_defconfig b/arch/arm/configs/tegra_whistler_android_defconfig
index 6894b5364305..fe2839172cd2 100644
--- a/arch/arm/configs/tegra_whistler_android_defconfig
+++ b/arch/arm/configs/tegra_whistler_android_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.29
-# Wed Jan 13 20:07:37 2010
+# Mon Jan 18 12:46:38 2010
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -1175,6 +1175,8 @@ CONFIG_UDC_FSL_NR_ENDPOINTS=32
#
# OTG and related infrastructure
#
+CONFIG_USB_OTG_UTILS=y
+CONFIG_USB_TEGRA_OTG=y
# CONFIG_USB_GPIO_VBUS is not set
CONFIG_MMC=y
CONFIG_EMBEDDED_MMC_START_OFFSET=y
diff --git a/arch/arm/mach-tegra/include/mach/platform.h b/arch/arm/mach-tegra/include/mach/platform.h
index f80632aff0a8..98064eab1bfa 100644
--- a/arch/arm/mach-tegra/include/mach/platform.h
+++ b/arch/arm/mach-tegra/include/mach/platform.h
@@ -21,6 +21,7 @@
#ifndef __MACH_TEGRA_PLATFORM_H
extern unsigned long tegra_get_module_inst_base(const char *, int);
+extern unsigned long tegra_get_module_inst_size(const char *, int);
extern unsigned int tegra_get_module_inst_irq(const char *, int, int);
#define TEGRA_PL310_BASE (tegra_get_module_inst_base("pl310", 0))
diff --git a/arch/arm/mach-tegra/include/nvddk_usbphy.h b/arch/arm/mach-tegra/include/nvddk_usbphy.h
index 283a5d9ac43c..ef1be673e8d4 100644..100755
--- a/arch/arm/mach-tegra/include/nvddk_usbphy.h
+++ b/arch/arm/mach-tegra/include/nvddk_usbphy.h
@@ -272,22 +272,24 @@ void NvDdkUsbPhyClose(NvDdkUsbPhyHandle hUsbPhy);
* reinitializing.
*
* @param hUsbPhy Handle acquired during the NvDdkUsbPhyOpen() call.
+ * @param IsHostMode indicates the host mode or not.
* @param IsDpd Deep sleep power up or not .
*
* @retval NvSuccess
* @retval NvError_Timeout If phy clock is not stable in expected time.
*/
-NvError NvDdkUsbPhyPowerUp(NvDdkUsbPhyHandle hUsbPhy, NvBool IsDpd);
+NvError NvDdkUsbPhyPowerUp(NvDdkUsbPhyHandle hUsbPhy, NvBool IsHostMode, NvBool IsDpd);
/**
* Powers down the PHY. It could be low power mode or shutdown.
*
* @param hUsbPhy Handle acquired during the NvDdkUsbPhyOpen() call.
+ * @param IsHostMode indicates the host mode or not.
* @param IsDpd Handle Deep sleep power down or not .
*
* @retval NvSuccess
*/
-NvError NvDdkUsbPhyPowerDown(NvDdkUsbPhyHandle hUsbPhy, NvBool IsDpd);
+NvError NvDdkUsbPhyPowerDown(NvDdkUsbPhyHandle hUsbPhy, NvBool IsHostMode, NvBool IsDpd);
/**
* Perform an I/O control operation on the device.
diff --git a/arch/arm/mach-tegra/init_common.c b/arch/arm/mach-tegra/init_common.c
index 32746e5e4d50..97809fc0d086 100644..100755
--- a/arch/arm/mach-tegra/init_common.c
+++ b/arch/arm/mach-tegra/init_common.c
@@ -30,6 +30,7 @@
#include "nvrm_init.h"
#include "nvrm_drf.h"
#include "mach/nvrm_linux.h"
+#include "mach/platform.h"
#include "nvos.h"
#include "nvutil.h"
#include "nvassert.h"
@@ -474,7 +475,8 @@ static void __init tegra_register_usb_gadget(void)
/* fixme: add ulpi here? */
p = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, i);
- if (!p || !(p->UsbMode & NvOdmUsbModeType_Device))
+ if (!p || !((p->UsbMode & NvOdmUsbModeType_Device) ||
+ (p->UsbMode & NvOdmUsbModeType_OTG)))
continue;
irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, mod, 0);
@@ -555,7 +557,9 @@ static void __init tegra_register_usb_host(void)
/* fixme: add ulpi here? */
p = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, i);
- if (!p || !(p->UsbMode & NvOdmUsbModeType_Host) || tegra_is_udc(mod))
+ if (!p || !((p->UsbMode & NvOdmUsbModeType_Host) ||
+ (p->UsbMode & NvOdmUsbModeType_OTG) ) ||
+ (tegra_is_udc(mod) && !(p->UsbMode & NvOdmUsbModeType_OTG)))
continue;
irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, mod, 0);
@@ -588,8 +592,71 @@ fail:
}
#endif
+#if !defined(CONFIG_USB_TEGRA_OTG)
+#define tegra_register_usb_otg() do {} while (0)
+#else
+
+static void __init tegra_register_usb_otg(void)
+{
+ NvU32 port_count, i;
+
+ port_count = NvRmModuleGetNumInstances(s_hRmGlobal, NvRmModuleID_Usb2Otg);
+
+ for (i=0; i<port_count; i++) {
+ const NvOdmUsbProperty *p;
+ struct platform_device *platdev = NULL;
+ struct resource *res;
+ struct tegra_otg_platform_data pdata;
+
+ p = NvOdmQueryGetUsbProperty(NvOdmIoModule_Usb, i);
+ if (!p || !(p->UsbMode & NvOdmUsbModeType_OTG))
+ continue;
+
+ platdev = platform_device_alloc("tegra-otg", i);
+ if (!platdev) {
+ pr_err("unable to allocate device for tegra-otg\n");
+ goto fail;
+ }
+
+ res = kzalloc(sizeof(struct resource)*2, GFP_KERNEL);
+ if (!res) {
+ pr_err("unable to allocate resource for tegra-otg\n");
+ goto fail;
+ }
+
+ res[0].flags = IORESOURCE_MEM;
+ res[0].start = tegra_get_module_inst_base("usbotg", i);
+ res[0].end = res[0].start + tegra_get_module_inst_size("usbotg", i) - 1;
+ res[1].flags = IORESOURCE_IRQ;
+ res[1].start = res[1].end = tegra_get_module_inst_irq("usbotg", i, 0);
+
+ if (platform_device_add_resources(platdev, res, 2)) {
+ pr_err("unable to add resources to tegra-otg device\n");
+ goto fail;
+ }
+
+ memset(&pdata, 0, sizeof(pdata));
+ pdata.instance = i;
+ pdata.usb_property = p;
+
+ if (platform_device_add_data(platdev, &pdata, sizeof(pdata))) {
+ pr_err("unable to add platform data to tegra-otg device\n");
+ goto fail;
+ }
+
+ if (platform_device_add(platdev)) {
+ pr_err("unable to add tegra-otg device\n");
+ goto fail;
+ }
+ }
+fail:
+ ;
+}
+#endif
+
void __init tegra_register_usb(void)
{
+ tegra_register_usb_otg();
tegra_register_usb_gadget();
tegra_register_usb_host();
}
diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
index 6b973c172746..fe3c3252d39f 100644..100755
--- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
+++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
@@ -134,7 +134,7 @@ UsbPhyPowerRailEnable(
if( !UsbPhyDiscover( pUsbPhy ) )
{
// Do nothing if no power rail info is discovered
- return;
+ return;
}
/* enable the power rail */
@@ -336,6 +336,39 @@ UsbPhyIoctlUsbBisuHintsOnOff(
return UsbPhyDfsBusyHint(pUsbPhy, pOnOff->OnOff, pOnOff->BoostDurationMs);
}
+/*
+ * NvDdkUsbPhyHelperThread() - Thread to control the Busy hints and Vbus.
+ *
+ * Busy hints and Vbus are controlled based on the USB cable connection status.
+ * USB cable status change(connect/disconnect) is identified in the ISR.
+ * 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. This helper thread is signaled from the ISR by calling Phy
+ * suspend/resume APIs to control the busy hints and VBUS.
+ */
+static void
+NvDdkUsbPhyHelperThread(
+ void* pArgs)
+{
+ NvDdkUsbPhyHandle hUsbPhy = (NvDdkUsbPhyHandle)pArgs;
+
+ hUsbPhy->Stopped = NV_FALSE;
+
+ while (!hUsbPhy->Stopped)
+ {
+ /* 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);
+ /* Turn on/off the vbus for host mode */
+ if (hUsbPhy->IsHostMode)
+ {
+ UsbPrivEnableVbus(hUsbPhy, hUsbPhy->IsPhyPoweredUp);
+ }
+ }
+}
+
NvError
NvDdkUsbPhyOpen(
NvRmDeviceHandle hRm,
@@ -459,7 +492,15 @@ NvDdkUsbPhyOpen(
NvRmPowerRegister(pUsbPhy->hRmDevice,
pUsbPhy->hPwrEventSem, &pUsbPhy->RmPowerClientId));
- if (pUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host)
+ NV_CHECK_ERROR_CLEANUP(
+ NvOsSemaphoreCreate(&pUsbPhy->HelperThreadSema, 0));
+
+ NV_CHECK_ERROR_CLEANUP(
+ NvOsThreadCreate(&NvDdkUsbPhyHelperThread,
+ (void*)pUsbPhy, &pUsbPhy->hThreadId));
+
+ if ((pUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) ||
+ (pUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_OTG))
{
UsbPrivEnableVbus(pUsbPhy, NV_TRUE);
}
@@ -471,6 +512,9 @@ NvDdkUsbPhyOpen(
// Open the H/W interface
UsbPhyOpenHwInterface(pUsbPhy);
+ /* enable the busy hints for USB */
+ UsbPhyDfsBusyHint(pUsbPhy, NV_TRUE, NV_WAIT_INFINITE);
+
// Initialize the USB Phy
NV_CHECK_ERROR_CLEANUP(UsbPhyInitialize(pUsbPhy));
}
@@ -542,13 +586,18 @@ NvDdkUsbPhyClose(
hUsbPhy->CloseHwInterface(hUsbPhy);
}
- if (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host)
+ if ((hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) ||
+ (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_OTG))
{
UsbPrivEnableVbus(hUsbPhy, NV_FALSE);
}
UsbPhyPowerRailEnable(hUsbPhy, NV_FALSE);
+ hUsbPhy->Stopped = NV_TRUE;
+ NvOsThreadJoin(hUsbPhy->hThreadId);
+ NvOsSemaphoreDestroy(hUsbPhy->HelperThreadSema);
+
NvRmPhysicalMemUnmap(
(void*)hUsbPhy->UsbVirAdr, hUsbPhy->UsbBankSize);
@@ -562,6 +611,7 @@ NvDdkUsbPhyClose(
NvError
NvDdkUsbPhyPowerUp(
NvDdkUsbPhyHandle hUsbPhy,
+ NvBool IsHostMode,
NvBool IsDpd)
{
NvError e = NvSuccess;
@@ -593,10 +643,12 @@ NvDdkUsbPhyPowerUp(
NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance),
hUsbPhy->RmPowerClientId, NV_TRUE));
}
-
+ // Power up the Phy
NV_CHECK_ERROR_CLEANUP(hUsbPhy->PowerUp(hUsbPhy));
-
hUsbPhy->IsPhyPoweredUp = NV_TRUE;
+ hUsbPhy->IsHostMode = IsHostMode;
+ // signal to set the busy hints and vbus
+ NvOsSemaphoreSignal(hUsbPhy->HelperThreadSema);
//NvOsDebugPrintf("NvDdkUsbPhyPowerUp::VOLTAGE ON\n");
@@ -609,6 +661,7 @@ fail:
NvError
NvDdkUsbPhyPowerDown(
NvDdkUsbPhyHandle hUsbPhy,
+ NvBool IsHostMode,
NvBool IsDpd)
{
NvError e = NvSuccess;
@@ -648,6 +701,8 @@ NvDdkUsbPhyPowerDown(
}
hUsbPhy->IsPhyPoweredUp = NV_FALSE;
+ hUsbPhy->IsHostMode = IsHostMode;
+ NvOsSemaphoreSignal(hUsbPhy->HelperThreadSema);
//NvOsDebugPrintf("NvDdkUsbPhyPowerDown::VOLTAGE OFF\n");
diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h b/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h
index 9d7fb7c8e8e7..741ea1674399 100644..100755
--- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h
+++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy_priv.h
@@ -126,6 +126,14 @@ typedef struct NvDdkUsbPhyRec
NvBool IsPhyPoweredUp;
// Utmpi Pad Config control structure
NvDdkUsbPhyUtmiPadConfig *pUtmiPadConfig;
+ // Thread ID for the helper thread
+ NvOsThreadHandle hThreadId;
+ // semphore for signaling the thread
+ NvOsSemaphoreHandle HelperThreadSema;
+ // variable to control the thread loop
+ NvBool Stopped;
+ // Indicates phy powered up for the host mode
+ NvBool IsHostMode;
// Set of function pointers to access the usb phy hardware interface.
// Pointer to the h/w specific PowerUp function.
NvError (*PowerUp)(NvDdkUsbPhyHandle hUsbPhy);
diff --git a/arch/arm/mach-tegra/tegra_sysmap.c b/arch/arm/mach-tegra/tegra_sysmap.c
index cb5b88ced984..55154b546b12 100644..100755
--- a/arch/arm/mach-tegra/tegra_sysmap.c
+++ b/arch/arm/mach-tegra/tegra_sysmap.c
@@ -36,6 +36,8 @@ static NvRmModuleID tegra_map_name_to_mod(const char *name, int inst)
return NVRM_MODULE_ID(NvRmPrivModuleID_ArmPerif, inst);
else if (!strcmp(name, "pcie"))
return NVRM_MODULE_ID(NvRmPrivModuleID_Pcie, inst);
+ else if (!strcmp(name, "usbotg"))
+ return NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, inst);
return (NvRmModuleID) 0;
}
@@ -55,6 +57,18 @@ unsigned long tegra_get_module_inst_base(const char *name, int inst)
return (unsigned long)phys;
}
+unsigned long tegra_get_module_inst_size(const char *name, int inst)
+{
+ NvRmModuleID mod_id = tegra_map_name_to_mod(name, inst);
+ NvRmPhysAddr phys = 0xffffffffUL;
+ NvU32 len = 0;
+
+ if (mod_id)
+ NvRmModuleGetBaseAddress(s_hRmGlobal, mod_id, &phys, &len);
+
+ return (unsigned long)len;
+}
+
unsigned int tegra_get_module_inst_irq(const char *name, int inst,
int mod_int_id)
{