summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rwxr-xr-x[-rw-r--r--]drivers/usb/gadget/fsl_udc_core.c146
-rwxr-xr-x[-rw-r--r--]drivers/usb/gadget/tegra_udc.c7
-rwxr-xr-x[-rw-r--r--]drivers/usb/host/ehci-tegra.c206
-rw-r--r--drivers/usb/host/ehci.h11
-rwxr-xr-x[-rw-r--r--]drivers/usb/otg/Kconfig9
-rwxr-xr-x[-rw-r--r--]drivers/usb/otg/Makefile1
-rwxr-xr-xdrivers/usb/otg/tegra-otg.c273
-rwxr-xr-x[-rw-r--r--]include/linux/tegra_devices.h8
15 files changed, 743 insertions, 87 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)
{
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index bbfedf5e9d20..a0753a9d9788 100644..100755
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -291,14 +291,16 @@ static void dr_controller_run(struct fsl_udc *udc)
#if defined(CONFIG_ARCH_TEGRA)
unsigned long timeout;
#define FSL_UDC_RUN_TIMEOUT 1000
-
- /* Enable cable detection interrupt, without setting the
- * USB_SYS_VBUS_WAKEUP_INT bit. USB_SYS_VBUS_WAKEUP_INT is
- * clear on write */
- temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
- temp |= (USB_SYS_VBUS_WAKEUP_INT_ENABLE | USB_SYS_VBUS_WAKEUP_ENABLE);
- temp &= ~USB_SYS_VBUS_WAKEUP_INT_STATUS;
- fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
+ /* If OTG transceiver is available, then it handles the VBUS detection */
+ if ( !udc_controller->transceiver) {
+ /* Enable cable detection interrupt, without setting the
+ * USB_SYS_VBUS_WAKEUP_INT bit. USB_SYS_VBUS_WAKEUP_INT is
+ * clear on write */
+ temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
+ temp |= (USB_SYS_VBUS_WAKEUP_INT_ENABLE | USB_SYS_VBUS_WAKEUP_ENABLE);
+ temp &= ~USB_SYS_VBUS_WAKEUP_INT_STATUS;
+ fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
+ }
#endif
@@ -1756,6 +1758,27 @@ static void reset_irq(struct fsl_udc *udc)
#endif
}
+#if defined(CONFIG_ARCH_TEGRA)
+/*
+ * Restart device controller in the OTG mode on VBUS detection
+ */
+static void fsl_udc_restart(struct fsl_udc *udc)
+{
+ /* setup the controller in the device mode */
+ dr_controller_setup(udc);
+ /* Reset all internal used Queues */
+ reset_queues(udc);
+ /* setup EP0 for setup packet */
+ ep0_setup(udc);
+ /* start the controller */
+ dr_controller_run(udc);
+ /* initialize the USB and EP states */
+ udc->usb_state = USB_STATE_ATTACHED;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = 0;
+}
+#endif
+
/*
* USB device controller interrupt handler
*/
@@ -1769,44 +1792,67 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
u32 temp;
#endif
- /* Disable ISR for OTG host mode */
- if (udc->stopped)
- return IRQ_NONE;
- spin_lock_irqsave(&udc->lock, flags);
- irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
- /* Clear notification bits */
- fsl_writel(irq_src, &dr_regs->usbsts);
-
- /* VDBG("irq_src [0x%8x]", irq_src); */
#if defined(CONFIG_ARCH_TEGRA)
- /* VBUS A session detection interrupts. When the interrupt is received,
- * the mark the vbus active shadow.
- */
- temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
- if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) {
- if (temp & USB_SYS_VBUS_STATUS) {
- udc->vbus_active = 1;
- //printk(KERN_INFO "USB cable connected\n");
+ spin_lock_irqsave(&udc->lock, flags);
+ /* check OTG tranceiver is available or not */
+ if (udc->transceiver) {
+ if (udc->transceiver->state == OTG_STATE_B_PERIPHERAL) {
+ if (!udc->vbus_active) {
+ /* set vbus active and enable the usb clocks */
+ udc->vbus_active = 1;
+ platform_udc_clk_resume();
+ fsl_udc_restart(udc);
+ }
+ } else if (udc->transceiver->state == OTG_STATE_A_SUSPEND) {
+ if (udc->vbus_active) {
+ /* stop the controller and turn off the clocks */
+ dr_controller_stop(udc);
+ platform_udc_clk_suspend();
+ udc->vbus_active = 0;
+ udc->usb_state = USB_STATE_DEFAULT;
+ udc->transceiver->state = OTG_STATE_UNDEFINED;
+ }
} else {
- reset_queues(udc);
- udc->vbus_active = 0;
- udc->usb_state = USB_STATE_DEFAULT;
- //printk("USB cable dis-connected\n");
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return IRQ_HANDLED;
}
+ }else {
+ /* VBUS A session detection interrupts. When the interrupt is received,
+ * mark the vbus active shadow.
+ */
+ temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
/* write back the register to clear the interrupt */
fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
-
- if (udc->vbus_active) {
- platform_udc_clk_resume();
- } else {
- platform_udc_clk_suspend();
+ if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) {
+ if (temp & USB_SYS_VBUS_STATUS) {
+ udc->vbus_active = 1;
+ platform_udc_clk_resume();
+ //printk(KERN_INFO "USB cable connected\n");
+ } else {
+ reset_queues(udc);
+ udc->vbus_active = 0;
+ udc->usb_state = USB_STATE_DEFAULT;
+ platform_udc_clk_suspend();
+ //printk("USB cable dis-connected\n");
+ }
+ status = IRQ_HANDLED;
}
-
- status = IRQ_HANDLED;
}
+ spin_unlock_irqrestore(&udc->lock, flags);
#endif
+ /* Disable ISR for OTG host mode */
+ if (udc->stopped)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
+ /* Clear notification bits */
+ fsl_writel(irq_src, &dr_regs->usbsts);
+
+ /* VDBG("irq_src [0x%8x]", irq_src); */
+
/* Need to resume? */
if (udc->usb_state == USB_STATE_SUSPENDED)
if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
@@ -1899,15 +1945,34 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
udc_controller->driver = NULL;
goto out;
}
+ printk(KERN_INFO "%s: bind to driver %s\n",
+ udc_controller->gadget.name, driver->driver.name);
+
+#if defined(CONFIG_ARCH_TEGRA)
+ if (udc_controller->transceiver) {
+ if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS)) {
+ /* If VBUS is not present then power down the clocks */
+ udc_controller->vbus_active = 0;
+ platform_udc_clk_suspend();
+ /* set the gadget driver and quit (don't run the controller) */
+ otg_set_peripheral(udc_controller->transceiver,
+ &udc_controller->gadget);
+ goto out;
+ } else {
+ /* VBUS detected set the gadget driver and run the controller */
+ otg_set_peripheral(udc_controller->transceiver,
+ &udc_controller->gadget);
+ udc_controller->transceiver->state = OTG_STATE_B_PERIPHERAL;
+ udc_controller->vbus_active = 1;
+ }
+ }
+#endif
/* Enable DR IRQ reg and Set usbcmd reg Run bit */
dr_controller_run(udc_controller);
udc_controller->usb_state = USB_STATE_ATTACHED;
udc_controller->ep0_state = WAIT_FOR_SETUP;
udc_controller->ep0_dir = 0;
- printk(KERN_INFO "%s: bind to driver %s\n",
- udc_controller->gadget.name, driver->driver.name);
-
out:
if (retval)
printk("gadget driver register failed %d\n", retval);
@@ -2504,6 +2569,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
create_proc_file();
#if defined(CONFIG_ARCH_TEGRA)
+ /* Get the OTG transceiver. If OTG is enabled then transceiver will be set
+ * otherwise transceiver will be NULL */
+ udc_controller->transceiver = otg_get_transceiver();
/* Power down the phy if cable is not connected */
if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS))
platform_udc_clk_suspend();
diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c
index ec4e19746022..c55712c3f9fc 100644..100755
--- a/drivers/usb/gadget/tegra_udc.c
+++ b/drivers/usb/gadget/tegra_udc.c
@@ -38,6 +38,9 @@ int tegra_udc_clk_init(struct platform_device *pdev)
if (nverr != NvSuccess)
return -ENODEV;
+ /* Power up the USB phy */
+ NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(s_hUsbPhy, NV_FALSE, 0));
+
return 0;
}
@@ -51,10 +54,10 @@ void tegra_udc_clk_release(void)
void tegra_udc_clk_suspend(void)
{
- NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(s_hUsbPhy, 0));
+ NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(s_hUsbPhy, NV_FALSE, 0));
}
void tegra_udc_clk_resume(void)
{
- NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(s_hUsbPhy, 0));
+ NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(s_hUsbPhy, NV_FALSE, 0));
}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 698e5f7f365e..71e59c4ee723 100644..100755
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -37,11 +37,100 @@
#include "nvrm_hardware_access.h"
#include "nvddk_usbphy.h"
-#define TEGRA_USB_ID_INT_ENABLE (1 << 0)
-#define TEGRA_USB_ID_INT_STATUS (1 << 1)
-#define TEGRA_USB_ID_PIN_STATUS (1 << 2)
-#define TEGRA_USB_ID_PIN_WAKEUP_ENABLE (1 << 6)
-#define TEGRA_USB_PHY_WAKEUP_REG_OFFSET (0x408)
+#define TEGRA_USB_ID_INT_ENABLE (1 << 0)
+#define TEGRA_USB_ID_INT_STATUS (1 << 1)
+#define TEGRA_USB_ID_PIN_STATUS (1 << 2)
+#define TEGRA_USB_ID_PIN_WAKEUP_ENABLE (1 << 6)
+#define TEGRA_USB_PHY_WAKEUP_REG_OFFSET (0x408)
+#define TEGRA_USB_USBMODE_REG_OFFSET (0x1a8)
+#define TEGRA_USB_USBMODE_HOST (3)
+
+
+static int tegra_ehci_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 __iomem *status_reg = &ehci->regs->port_status[
+ (wIndex & 0xff) - 1];
+ u32 temp;
+ struct tegra_hcd_platform_data *pdata;
+ unsigned long flags;
+ int retval = 0;
+
+ /* initialize the platform data pointer */
+ pdata = hcd->self.controller->platform_data;
+
+ /* In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits
+ * that are write on clear, by wrting back the register read value, so
+ * USB_PORT_FEAT_ENABLE is handled here by masking the set on clear bits */
+ if ((typeReq == ClearPortFeature) && (wValue == USB_PORT_FEAT_ENABLE)) {
+ spin_lock_irqsave (&ehci->lock, flags);
+ temp = ehci_readl(ehci, status_reg);
+ ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~PORT_PE, status_reg);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return retval;
+ }
+
+ /* Handle the hub control events here */
+ retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+
+ /* Power down the USB phy when there is no port connection and all
+ * HUB events are cleared by checking the lower four bits
+ * (PORT_CONNECT | PORT_CSC | PORT_PE | PORT_PEC) */
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG)
+ && ehci->transceiver) {
+ if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) {
+ temp = ehci_readl(ehci, status_reg);
+ if (!(temp & (PORT_CONNECT | PORT_CSC | PORT_PE | PORT_PEC))
+ && ehci->host_reinited) {
+ NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0);
+ ehci->transceiver->state = OTG_STATE_UNDEFINED;
+ ehci->host_reinited = 0;
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+static void tegra_ehci_restart (struct usb_hcd *hcd)
+{
+ unsigned int temp;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ /* Set to Host mode by setting bit 0-1 of USB device mode register */
+ temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET);
+ writel((temp | TEGRA_USB_USBMODE_HOST),
+ (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET));
+
+ /* reset the ehci controller */
+ ehci->controller_resets_phy = 0;
+ ehci_reset(ehci);
+ ehci->controller_resets_phy = 1;
+ /* setup the frame list and Async q heads */
+ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+ ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
+ /* setup the command register and set the controller in RUN mode */
+ ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+ ehci->command |= CMD_RUN;
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+ down_write(&ehci_cf_port_reset_rwsem);
+ hcd->state = HC_STATE_RUNNING;
+ /* unblock posted writes */
+ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+ ehci_readl(ehci, &ehci->regs->command);
+ up_write(&ehci_cf_port_reset_rwsem);
+
+ /* Turn On Interrupts */
+ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
+}
static void tegra_ehci_shutdown (struct usb_hcd *hcd)
{
@@ -51,13 +140,14 @@ static void tegra_ehci_shutdown (struct usb_hcd *hcd)
/* ehci_shutdown touches the USB controller registers, make sure
* controller has clocks to it, if controller is already in power up
* status, calling the NvDdkUsbPhyPowerUp will just return */
- NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(pdata->hUsbPhy, 0));
+ NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0));
/* call ehci shut down */
ehci_shutdown(hcd);
/* we are ready to shut down, powerdown the phy */
- NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(pdata->hUsbPhy, 0));
+ NV_ASSERT_SUCCESS(NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0));
}
+
static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
@@ -68,19 +158,38 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
spin_lock (&ehci->lock);
- if (pdata->pUsbProperty->IdPinDetectionType ==
- NvOdmUsbIdPinType_CableId) {
- /* read otgsc register for ID pin status change */
- status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
- writel(status, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET));
-
- /* Check if there is any ID pin interrupt */
- if (status & TEGRA_USB_ID_INT_STATUS) {
- /* Check pin status and enable/disable the power */
- if (status & TEGRA_USB_ID_PIN_STATUS) {
- NvDdkUsbPhyPowerDown(pdata->hUsbPhy, 0);
- } else {
- NvDdkUsbPhyPowerUp(pdata->hUsbPhy, 0);
+ if ((pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG)
+ && ehci->transceiver) {
+ if (ehci->transceiver->state == OTG_STATE_A_HOST) {
+ if (!ehci->host_reinited) {
+ ehci->host_reinited = 1;
+ NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0);
+ tegra_ehci_restart(hcd);
+ }
+ } else if (ehci->transceiver->state == OTG_STATE_A_SUSPEND) {
+ if (!ehci->host_reinited) {
+ spin_unlock (&ehci->lock);
+ return IRQ_HANDLED;
+ }
+ } else {
+ spin_unlock (&ehci->lock);
+ return IRQ_HANDLED;
+ }
+ } else {
+ if (pdata->pUsbProperty->IdPinDetectionType ==
+ NvOdmUsbIdPinType_CableId) {
+ /* read otgsc register for ID pin status change */
+ status = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
+ writel(status, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET));
+
+ /* Check if there is any ID pin interrupt */
+ if (status & TEGRA_USB_ID_INT_STATUS) {
+ /* Check pin status and enable/disable the power */
+ if (status & TEGRA_USB_ID_PIN_STATUS) {
+ NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0);
+ } else {
+ NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0);
+ }
}
}
}
@@ -90,6 +199,7 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
return ehci_irq(hcd);
}
+
static int tegra_ehci_reinit(struct usb_hcd *hcd)
{
struct tegra_hcd_platform_data *pdata;
@@ -99,6 +209,7 @@ static int tegra_ehci_reinit(struct usb_hcd *hcd)
NV_CHECK_ERROR_CLEANUP(NvDdkUsbPhyOpen(s_hRmGlobal,
pdata->instance, &pdata->hUsbPhy));
+ NV_CHECK_ERROR_CLEANUP(NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0));
return 0;
@@ -178,7 +289,7 @@ static const struct hc_driver tegra_ehci_hc_driver = {
.endpoint_disable = ehci_endpoint_disable,
.get_frame_number = ehci_get_frame,
.hub_status_data = ehci_hub_status_data,
- .hub_control = ehci_hub_control,
+ .hub_control = tegra_ehci_hub_control,
.bus_suspend = tegra_ehci_bus_suspend,
.bus_resume = tegra_ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
@@ -266,8 +377,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
hcd->regs = vaddr;
/* Set to Host mode by setting bit 0-1 of USB device mode register */
- temp = readl(hcd->regs + 0x1a8);
- writel((temp | 0x3), (hcd->regs + 0x1a8));
+ temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET);
+ writel((temp | TEGRA_USB_USBMODE_HOST),
+ (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET));
irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal,
NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, instance), 0);
@@ -280,19 +392,43 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto fail;
platform_set_drvdata(pdev, hcd);
- if (pdata->pUsbProperty->IdPinDetectionType ==
- NvOdmUsbIdPinType_CableId) {
- /* enable the cable ID interrupt */
- temp = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
- temp |= TEGRA_USB_ID_INT_ENABLE;
- temp |= TEGRA_USB_ID_PIN_WAKEUP_ENABLE;
- writel(temp, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET));
-
- /* Check if we detect any device connected */
- if (temp & TEGRA_USB_ID_PIN_STATUS) {
- NvDdkUsbPhyPowerDown(pdata->hUsbPhy, 0);
+ if (pdata->pUsbProperty->UsbMode == NvOdmUsbModeType_OTG) {
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ ehci->transceiver = otg_get_transceiver();
+ if (ehci->transceiver) {
+ otg_set_host(ehci->transceiver, (struct usb_bus *)hcd);
+ /* Stop the controller and power down the phy, OTG will start the
+ * host driver based on the ID pin detection */
+ ehci_halt(ehci);
+ /* reset the host and put the controller in idle mode */
+ temp = ehci_readl(ehci, &ehci->regs->command);
+ temp |= CMD_RESET;
+ ehci_writel(ehci, temp, &ehci->regs->command);
+ temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET);
+ writel((temp & ~TEGRA_USB_USBMODE_HOST),
+ (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET));
+ NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0);
+ ehci->host_reinited = 0;
} else {
- NvDdkUsbPhyPowerUp(pdata->hUsbPhy, 0);
+ dev_err(&pdev->dev, "Cannot get OTG transceiver\n");
+ e = -ENODEV;
+ goto fail;
+ }
+ } else {
+ if (pdata->pUsbProperty->IdPinDetectionType ==
+ NvOdmUsbIdPinType_CableId) {
+ /* enable the cable ID interrupt */
+ temp = readl(hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
+ temp |= TEGRA_USB_ID_INT_ENABLE;
+ temp |= TEGRA_USB_ID_PIN_WAKEUP_ENABLE;
+ writel(temp, (hcd->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET));
+
+ /* Check if we detect any device connected */
+ if (temp & TEGRA_USB_ID_PIN_STATUS) {
+ NvDdkUsbPhyPowerDown(pdata->hUsbPhy, NV_TRUE, 0);
+ } else {
+ NvDdkUsbPhyPowerUp(pdata->hUsbPhy, NV_TRUE, 0);
+ }
}
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 3126273c95ea..091e26e40850 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -106,6 +106,13 @@ struct ehci_hcd { /* one per controller */
unsigned long suspended_ports; /* which ports are
suspended */
+#ifdef CONFIG_USB_OTG_UTILS
+ /*
+ * Transceiver decleration for OTG support;
+ */
+ struct otg_transceiver *transceiver;
+#endif
+
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
struct dma_pool *qtd_pool; /* one or more per qh */
@@ -126,6 +133,7 @@ struct ehci_hcd { /* one per controller */
unsigned big_endian_desc:1;
unsigned has_amcc_usb23:1;
unsigned controller_resets_phy:1;
+ unsigned host_reinited:1;
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
@@ -230,6 +238,9 @@ static void free_cached_itd_list(struct ehci_hcd *ehci);
/*-------------------------------------------------------------------------*/
#include <linux/usb/ehci_def.h>
+#ifdef CONFIG_USB_OTG_UTILS
+#include <linux/usb/otg.h>
+#endif
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index ee55b449ffde..0aed0b7b7fa5 100644..100755
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -17,6 +17,15 @@ if USB || USB_GADGET
#
# USB Transceiver Drivers
#
+config USB_TEGRA_OTG
+ boolean "NVIDIA Tegra OTG support"
+ depends on USB && ARCH_TEGRA && USB_EHCI_HCD && USB_GADGET_TEGRA
+ select USB_OTG_UTILS
+ help
+ This driver enables 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.
+
config USB_GPIO_VBUS
tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
depends on GENERIC_GPIO
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index d73c7cf5e2f7..f1119a773efb 100644..100755
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_USB_OTG_UTILS) += otg.o
# transceiver drivers
+obj-$(CONFIG_USB_TEGRA_OTG) += tegra-otg.o
obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c
new file mode 100755
index 000000000000..832939e1875d
--- /dev/null
+++ b/drivers/usb/otg/tegra-otg.c
@@ -0,0 +1,273 @@
+/*
+ * tegra-otg.c
+ *
+ * OTG driver for detecting the USB ID and VBUS for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/tegra_devices.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include "../core/hcd.h"
+#include "mach/nvrm_linux.h"
+
+#define TEGRA_USB_ID_INT_ENABLE (1 << 0)
+#define TEGRA_USB_ID_INT_STATUS (1 << 1)
+#define TEGRA_USB_ID_STATUS (1 << 2)
+#define TEGRA_USB_ID_PIN_WAKEUP_ENABLE (1 << 6)
+#define TEGRA_USB_VBUS_WAKEUP_ENABLE (1 << 30)
+#define TEGRA_USB_VBUS_INT_ENABLE (1 << 8)
+#define TEGRA_USB_VBUS_INT_STATUS (1 << 9)
+#define TEGRA_USB_VBUS_STATUS (1 << 10)
+#define TEGRA_USB_WAKEUP_REG_OFFSET (0x408)
+
+static const char driver_name[] = "tegra-otg";
+
+/*
+ * Needs to be loaded before the UDC and Host driver that will use it.
+ */
+struct tegra_otg_data {
+ struct otg_transceiver otg;
+ struct device *dev;
+ spinlock_t lock;
+ int irq; /* irq allocated */
+ void __iomem *regs; /* device memory/io */
+ int instance; /* instance number of the controller */
+ NvDdkUsbPhyHandle usb_phy; /* handle to the USB phy */
+};
+
+static struct tegra_otg_data *sg_tegra_otg = NULL;
+
+
+/* VBUS change IRQ handler */
+static irqreturn_t tegra_otg_irq(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = (struct usb_hcd *)tegra_otg->otg.host;
+ unsigned int status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tegra_otg->lock, flags);
+
+ status = readl(tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET);
+ writel(status, tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET);
+
+ if (tegra_otg->otg.host) {
+ /* Check if there is any ID pin interrupt */
+ if (status & TEGRA_USB_ID_INT_STATUS) {
+ if (status & TEGRA_USB_ID_STATUS) {
+ tegra_otg->otg.state = OTG_STATE_A_SUSPEND;
+ } else {
+ tegra_otg->otg.state = OTG_STATE_A_HOST;
+ hcd->state = HC_STATE_RUNNING;
+ }
+ }
+ }
+
+ if (tegra_otg->otg.gadget && (tegra_otg->otg.state != OTG_STATE_A_HOST)) {
+ if (status & TEGRA_USB_VBUS_INT_STATUS) {
+ if (status & TEGRA_USB_VBUS_STATUS) {
+ tegra_otg->otg.state = OTG_STATE_B_PERIPHERAL;
+ } else {
+ tegra_otg->otg.state = OTG_STATE_A_SUSPEND;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&tegra_otg->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/* OTG transceiver interface */
+static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
+ struct usb_gadget *gadget)
+{
+ unsigned int temp;
+ unsigned long flags;
+
+ otg->gadget = gadget;
+ temp = readl(sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET);
+ temp |= (TEGRA_USB_VBUS_INT_ENABLE | TEGRA_USB_VBUS_WAKEUP_ENABLE);
+ temp &= ~TEGRA_USB_VBUS_INT_STATUS;
+ writel(temp, (sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET));
+
+ spin_lock_irqsave(&sg_tegra_otg->lock, flags);
+ /* Check if we detect any device connected */
+ if (!(temp & TEGRA_USB_ID_STATUS)) {
+ otg->state = OTG_STATE_A_HOST;
+ NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0);
+ }
+ spin_unlock_irqrestore(&sg_tegra_otg->lock, flags);
+
+ return 0;
+}
+
+static int tegra_otg_set_host(struct otg_transceiver *otg,
+ struct usb_bus *host)
+{
+ unsigned int temp;
+ struct tegra_otg_platform_data *pdata;
+
+ pdata = sg_tegra_otg->dev->platform_data;
+ otg->host = host;
+
+ if (pdata->usb_property->IdPinDetectionType ==
+ NvOdmUsbIdPinType_CableId) {
+ /* enable the cable ID interrupt */
+ temp = readl(sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET);
+ temp |= (TEGRA_USB_ID_INT_ENABLE | TEGRA_USB_ID_PIN_WAKEUP_ENABLE);
+ writel(temp, (sg_tegra_otg->regs + TEGRA_USB_WAKEUP_REG_OFFSET));
+ }
+
+ return 0;
+}
+
+/* effective for B devices, ignored for A-peripheral */
+static int tegra_otg_set_power(struct otg_transceiver *otg, unsigned mA)
+{
+ /* Draw from the Host in device mode */
+
+ return 0;
+}
+
+/* for non-OTG B devices: set/clear transceiver suspend mode */
+static int tegra_otg_set_suspend(struct otg_transceiver *otg, int suspend)
+{
+ /* Draw 0mA in the suspend mode */
+ return 0;
+}
+
+/* platform driver interface */
+static int __init tegra_otg_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct resource *res;
+ int instance = pdev->id;
+ NvError e;
+
+ sg_tegra_otg = kzalloc(sizeof(struct tegra_otg_data), GFP_KERNEL);
+ if (!sg_tegra_otg)
+ return -ENOMEM;
+
+ spin_lock_init(&sg_tegra_otg->lock);
+ platform_set_drvdata(pdev, sg_tegra_otg);
+
+ NV_CHECK_ERROR_CLEANUP(
+ NvDdkUsbPhyOpen(s_hRmGlobal, instance, &sg_tegra_otg->usb_phy)
+ );
+ NV_CHECK_ERROR_CLEANUP(
+ NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_TRUE, 0)
+ );
+ sg_tegra_otg->instance = pdev->id;
+ sg_tegra_otg->dev = &pdev->dev;
+ sg_tegra_otg->otg.label = driver_name;
+ sg_tegra_otg->otg.state = OTG_STATE_UNDEFINED;
+ sg_tegra_otg->otg.set_peripheral = tegra_otg_set_peripheral;
+ sg_tegra_otg->otg.set_host = tegra_otg_set_host;
+ sg_tegra_otg->otg.set_power = tegra_otg_set_power;
+ sg_tegra_otg->otg.set_suspend = tegra_otg_set_suspend;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENXIO;
+ goto err_irq;
+ }
+
+ sg_tegra_otg->regs = ioremap(res->start, resource_size(res));
+ if (!sg_tegra_otg->regs) {
+ err = -ENOMEM;
+ goto err_irq;
+ }
+
+ sg_tegra_otg->irq = platform_get_irq(pdev, 0);
+ if (!sg_tegra_otg->irq) {
+ err = -ENODEV;
+ goto err_irq;
+ }
+
+ err = request_irq(sg_tegra_otg->irq, tegra_otg_irq, IRQF_SHARED,
+ driver_name, pdev);
+ if (err) {
+ printk("cannot request irq %d err %d\n",
+ sg_tegra_otg->irq, err);
+ goto err_mem_map;
+ }
+
+ /* only active when a gadget is registered */
+ err = otg_set_transceiver(&sg_tegra_otg->otg);
+ if (err) {
+ dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
+ err);
+ goto err_otg;
+ }
+
+ NvDdkUsbPhyPowerDown(sg_tegra_otg->usb_phy, NV_TRUE, 0);
+
+ return 0;
+
+err_otg:
+ free_irq(sg_tegra_otg->irq, &pdev->dev);
+err_mem_map:
+ iounmap(sg_tegra_otg->regs);
+err_irq:
+ NvDdkUsbPhyClose(sg_tegra_otg->usb_phy);
+fail:
+ platform_set_drvdata(pdev, NULL);
+ kfree(sg_tegra_otg);
+ return err;
+}
+
+static int __exit tegra_otg_remove(struct platform_device *pdev)
+{
+ struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
+
+ otg_set_transceiver(NULL);
+ free_irq(tegra_otg->irq, &pdev->dev);
+ iounmap(tegra_otg->regs);
+ NvDdkUsbPhyClose(tegra_otg->usb_phy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(tegra_otg);
+ sg_tegra_otg = NULL;
+
+ return 0;
+}
+
+static struct platform_driver tegra_otg_driver = {
+ .driver = {
+ .name = driver_name,
+ },
+ .remove = __exit_p(tegra_otg_remove),
+ .probe = tegra_otg_probe,
+};
+
+static int __init tegra_otg_init(void)
+{
+ return platform_driver_register(&tegra_otg_driver);
+}
+module_init(tegra_otg_init);
+
+static void __exit tegra_otg_exit(void)
+{
+ platform_driver_unregister(&tegra_otg_driver);
+}
+module_exit(tegra_otg_exit);
+
+MODULE_DESCRIPTION("Tegra OTG driver");
diff --git a/include/linux/tegra_devices.h b/include/linux/tegra_devices.h
index 93d7324d2e37..143037a4a64e 100644..100755
--- a/include/linux/tegra_devices.h
+++ b/include/linux/tegra_devices.h
@@ -48,10 +48,16 @@ struct tegra_hcd_platform_data {
* macrocell interface) PHY on USB controllers 0 and 2. These 2 PHYs
* have its own rails.
*/
- NvU32 phyPowerRail;
+ NvU32 phyPowerRail;
NvDdkUsbPhyHandle hUsbPhy;
};
+/* Platform data for USB OTG driver */
+struct tegra_otg_platform_data {
+ NvU32 instance;
+ const NvOdmUsbProperty *usb_property;
+};
+
/* Platfrom data for I2C bus driver */
struct tegra_i2c_platform_data {
NvU32 IoModuleID;