Пример #1
0
static irqreturn_t tegra_otg_irq(int irq, void *data)
{
	struct tegra_otg_data *tegra = data;
	unsigned long flags;
	unsigned long val;

	spin_lock_irqsave(&tegra->lock, flags);

	val = otg_readl(tegra, USB_PHY_WAKEUP);
	if (val & (USB_VBUS_INT_EN | USB_ID_INT_EN)) {
		otg_writel(tegra, val, USB_PHY_WAKEUP);
		if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
			tegra->int_status = val;
			tegra->detect_vbus = false;
			schedule_work(&tegra->work);
		}
	} else {
		if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
			printk(KERN_INFO "%s(): WRONG! val = %#X\n", __func__, val);
			val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN);
			val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
			otg_writel(tegra, val, USB_PHY_WAKEUP);

			tegra->int_status = val;
			tegra->detect_vbus = false;
			schedule_work(&tegra->work);
		}
	}

	spin_unlock_irqrestore(&tegra->lock, flags);

	return IRQ_HANDLED;
}
Пример #2
0
static void tegra_otg_resume(struct device *dev)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct tegra_otg_data *tegra = platform_get_drvdata(pdev);
    int val;
    unsigned long flags;

    clk_enable(tegra->clk);

    /* Following delay is intentional.
     * It is placed here after observing system hang.
     * Root cause is not confirmed.
     */
    msleep(100);

    clk_enable(tegra->clk);
    spin_lock_irqsave(&tegra->lock, flags);
    val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET) | tegra->intr_reg_data;
    otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
    spin_unlock_irqrestore(&tegra->lock, flags);

    /* Delay a bit toensure registers are updated */
    udelay(1);

    clk_disable(tegra->clk);

    /* Check if we are in device mode with something plugged to us */
    tegra_otg_check_status();
}
Пример #3
0
static int tegra_otg_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct tegra_otg_data *tegra = platform_get_drvdata(pdev);
	struct otg_transceiver *otg = &tegra->otg;
	int val;
	DBG("%s(%d) BEGIN state : %s\n", __func__, __LINE__,
					tegra_state_name(otg->state));

	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	val &= ~(USB_ID_INT_EN | USB_VBUS_INT_EN);
	otg_writel(tegra, val, USB_PHY_WAKEUP);
	clk_disable(tegra->clk);

	/* Suspend peripheral mode, host mode is taken care by host driver */
	/* htc host mode change otg state in other function */
	/*if (otg->state == OTG_STATE_B_PERIPHERAL)
		tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND);*/

	tegra->suspended = true;

	DBG("%s(%d) END\n", __func__, __LINE__);
	return 0;
}
Пример #4
0
static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
				struct usb_gadget *gadget)
{
	struct tegra_otg_data *tegra;
	unsigned long val;

	tegra = container_of(otg, struct tegra_otg_data, otg);
	otg->gadget = gadget;

	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN);
	val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
	otg_writel(tegra, val, USB_PHY_WAKEUP);
	clk_disable(tegra->clk);

	if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS)) {
		val |= USB_VBUS_INT_STATUS;
	} else if (!(val & USB_ID_STATUS)) {
		val |= USB_ID_INT_STATUS;
	} else {
		val &= ~(USB_ID_INT_STATUS | USB_VBUS_INT_STATUS);
	}

	if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
		tegra->int_status = val;
		tegra->detect_vbus = false;
		schedule_work (&tegra->work);
	}

	return 0;
}
Пример #5
0
static void tegra_otg_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct tegra_otg_data *tegra = platform_get_drvdata(pdev);
	int val;
	unsigned long flags;
	DBG("%s(%d) BEGIN\n", __func__, __LINE__);

	if (!tegra->suspended)
		return;

	/* Clear pending interrupts */
	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	otg_writel(tegra, val, USB_PHY_WAKEUP);
	DBG("%s(%d) PHY WAKEUP register : 0x%x\n", __func__, __LINE__, val);
	clk_disable(tegra->clk);

	/* Enable interrupt and call work to set to appropriate state */
	spin_lock_irqsave(&tegra->lock, flags);
	if (tegra->builtin_host)
		tegra->int_status = val | USB_INT_EN;
	else
		tegra->int_status = val | USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN |
			USB_ID_PIN_WAKEUP_EN;

	spin_unlock_irqrestore(&tegra->lock, flags);
	schedule_work(&tegra->work);

	enable_interrupt(tegra, true);

	tegra->suspended = false;

	DBG("%s(%d) END\n", __func__, __LINE__);
}
Пример #6
0
static void tegra_otg_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct tegra_otg_data *tegra = platform_get_drvdata(pdev);
	struct otg_transceiver *otg = &tegra->otg;
	int val;
	unsigned long flags;
	DBG("%s(%d) BEGIN\n", __func__, __LINE__);

	/* Clear pending interrupts */
	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	otg_writel(tegra, val, USB_PHY_WAKEUP);
	DBG("%s(%d) PHY WAKEUP register : 0x%x\n", __func__, __LINE__, val);
	clk_disable(tegra->clk);

	/* Handle if host cable is replaced with device during suspend state */
	if (otg->state == OTG_STATE_A_HOST && (val & USB_ID_STATUS))
		tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND);

	/* Enable interrupt and call work to set to appropriate state */
	spin_lock_irqsave(&tegra->lock, flags);
	tegra->int_status = (val | USB_INT_EN);
	spin_unlock_irqrestore(&tegra->lock, flags);
	irq_work(&tegra->work);

	enable_interrupt(tegra, true);
	DBG("%s(%d) END\n", __func__, __LINE__);
}
Пример #7
0
static void tegra_recover_register(struct tegra_otg_data *tegra)
{
	unsigned long reg_val = 0;
	unsigned long flags = 0;
	int try_times = 5;

	while (try_times--) {
		reg_val = otg_readl(tegra, USB_PHY_WAKEUP);
		if (tegra_check_register(reg_val))
			break;
		msleep(20);
	}

	if (try_times < 0) {
		spin_lock_irqsave(&tegra->lock, flags);
		reg_val = otg_readl(tegra, USB_PHY_WAKEUP);
		tegra_recover_register_value(&reg_val);
		otg_writel(tegra, reg_val, USB_PHY_WAKEUP);
		spin_unlock_irqrestore(&tegra->lock, flags);
		msleep(100);
		reg_val = otg_readl(tegra, USB_PHY_WAKEUP);
	}
	dev_info(tegra->otg.dev, "%s reg_val=%lu\n", __func__, reg_val);
	return;
}
Пример #8
0
static irqreturn_t tegra_otg_irq(int irq, void *data)
{
	struct tegra_otg_data *tegra = data;
	unsigned long flags;
	unsigned long val;
	if (irq_otg_debug==1) {
		USB_INFO("otg_irq");
		irq_otg_debug = 0;
	}

	spin_lock_irqsave(&tegra->lock, flags);

	val = otg_readl(tegra, USB_PHY_WAKEUP);
	if (val & (USB_VBUS_INT_EN | USB_ID_INT_EN)) {
		otg_writel(tegra, val, USB_PHY_WAKEUP);
		if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
			tegra->int_status = val;
			schedule_work(&tegra->work);
		}
	}

	spin_unlock_irqrestore(&tegra->lock, flags);

	return IRQ_HANDLED;
}
Пример #9
0
static void tegra_otg_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
	int val;
	unsigned long flags;
	USB_INFO("%s\n",__func__);
	tegra_otg_enable_clk();

	/* Following delay is intentional.
	 * It is placed here after observing system hang.
	 * Root cause is not confirmed.
	 */
	msleep(1);
	/* restore the interupt enable for cable ID and VBUS */
	clk_enable(tegra_otg->clk);
	val = readl(tegra_otg->regs + USB_PHY_WAKEUP);
	writel(val, (tegra_otg->regs + USB_PHY_WAKEUP));

	val = otg_readl(tegra_otg, USB_PHY_WAKEUP);
	val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN);
	val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
	otg_writel(tegra_otg, val, USB_PHY_WAKEUP);

	val = readl(tegra_otg->regs + USB_PHY_WAKEUP);
	clk_disable(tegra_otg->clk);

	spin_lock_irqsave(&tegra_otg->lock, flags);
	if (tps80031_vbus_on)
		val |= USB_VBUS_STATUS;
	else
		val &= ~USB_VBUS_STATUS;
	tegra_otg->int_status = (val | USB_VBUS_INT_STATUS );
	spin_unlock_irqrestore(&tegra_otg->lock, flags);
	schedule_work(&tegra_otg->work);
#if 0
	/* A device might be connected while CPU is in sleep mode. In this case no interrupt
	 * will be triggered
	 * force irq_work to recheck connected devices
	 */
	if (!(val & USB_ID_STATUS)) {
		spin_lock_irqsave(&tegra_otg->lock, flags);
		tegra_otg->int_status = (val | USB_ID_INT_STATUS );
		spin_unlock_irqrestore(&tegra_otg->lock, flags);
		schedule_work(&tegra_otg->work);
	}
#endif
	return;
}
Пример #10
0
static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en)
{
	unsigned long val;

	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	if (en)
		val |= USB_INT_EN;
	else
		val &= ~USB_INT_EN;
	otg_writel(tegra, val, USB_PHY_WAKEUP);
	/* Add delay to make sure register is updated */
	udelay(1);
	clk_disable(tegra->clk);

	return val;
}
Пример #11
0
static int tegra_otg_set_host(struct otg_transceiver *otg,
				struct usb_bus *host)
{
	struct tegra_otg_data *tegra;
	unsigned long val;

	tegra = container_of(otg, struct tegra_otg_data, otg);
	otg->host = host;

	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	val &= ~(USB_VBUS_INT_STATUS | USB_ID_INT_STATUS);

	val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
	otg_writel(tegra, val, USB_PHY_WAKEUP);
	clk_disable(tegra->clk);

	return 0;
}
Пример #12
0
static irqreturn_t tegra_otg_irq(int irq, void *data)
{
	struct tegra_otg_data *tegra = data;
	unsigned long flags;
	unsigned long val;

	spin_lock_irqsave(&tegra->lock, flags);

	val = otg_readl(tegra, USB_PHY_WAKEUP);
	otg_writel(tegra, val, USB_PHY_WAKEUP);

	if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
		tegra->int_status = val;
		tegra->detect_vbus = false;
		schedule_work(&tegra->work);
	}

	spin_unlock_irqrestore(&tegra->lock, flags);

	return IRQ_HANDLED;
}
Пример #13
0
static irqreturn_t tegra_otg_irq(int irq, void *data)
{
	struct tegra_otg_data *tegra = data;
	unsigned long flags;
	unsigned long val;

	spin_lock_irqsave(&tegra->lock, flags);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	DBG("%s(%d) interrupt val = 0x%lx\n", __func__, __LINE__, val);

	if (val & (USB_VBUS_INT_EN | USB_ID_INT_EN)) {
		DBG("%s(%d) PHY_WAKEUP = 0x%lx\n", __func__, __LINE__, val);
		otg_writel(tegra, val, USB_PHY_WAKEUP);
		if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
			tegra->int_status = val;
			schedule_work(&tegra->work);
		}
	}
	spin_unlock_irqrestore(&tegra->lock, flags);

	return IRQ_HANDLED;
}
Пример #14
0
static int tegra_otg_set_host(struct otg_transceiver *otg,
				struct usb_bus *host)
{
	struct tegra_otg_data *tegra;
	unsigned long val;

	tegra = container_of(otg, struct tegra_otg_data, otg);
	otg->host = host;

	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	val &= ~(USB_VBUS_INT_STATUS | USB_ID_INT_STATUS);

	val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
	otg_writel(tegra, val, USB_PHY_WAKEUP);
#ifdef CONFIG_MACH_SAMSUNG_VARIATION_TEGRA
	tegra_recover_register(tegra);
#endif
	clk_disable(tegra->clk);

	return 0;
}
Пример #15
0
static irqreturn_t tegra_otg_irq(int irq, void *data)
{
    struct tegra_otg_data *tegra = data;
    unsigned long val;
    unsigned long flags;

    /* Get the interrupt cause and clean it - Clocks are already enabled.
       Don't try to enable them here, as those functions can sleep and that
       is not allowed in an interrupt ISR */
    spin_lock_irqsave(&tegra->lock, flags);
    val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
    otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
    spin_unlock_irqrestore(&tegra->lock, flags);

    /* We are interested in the VBUS change, as the cableID is
       not usable */

    /* If VBUS interrupts are enabled and we are in gadget mode and a change in VBUS detection is found */
    if ((val & TEGRA_VBUS_INT_EN) &&
            !tegra->host_mode &&
            (val & TEGRA_VBUS_INT_STATUS)) {

        /* Based on the VBUS status, switch to the right mode */
        unsigned long event = (val & TEGRA_VBUS_STATUS) ? USB_EVENT_VBUS : 0;

        /* If an event change was detected */
        if (tegra->event != event) {

            /* Store the new event */
            tegra->event = event;

            /* Schedule work */
            schedule_work(&tegra->work);
        }
    }

    return IRQ_HANDLED;
}
Пример #16
0
static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en)
{
	unsigned long val;

	clk_enable(tegra->clk);
	val = otg_readl(tegra, USB_PHY_WAKEUP);
	if (en) {
		val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN);
		val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
	} else {
		val &= ~(USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN);
		val &= ~(USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
	}
	otg_writel(tegra, val, USB_PHY_WAKEUP);
#ifdef CONFIG_MACH_SAMSUNG_VARIATION_TEGRA
	tegra_recover_register(tegra);
#endif
	/* Add delay to make sure register is updated */
	udelay(1);
	clk_disable(tegra->clk);

	return val;
}
Пример #17
0
static int tegra_otg_probe(struct platform_device *pdev)
{
    struct tegra_otg_data *tegra;
    struct resource *res;
    unsigned long val;
    int err;
    struct tegra_otg_platform_data *pdata = pdev->dev.platform_data;

    if (!pdata) {
        dev_err(&pdev->dev, "No platform data\n");
        return -ENODEV;
    }

    tegra = kzalloc(sizeof(struct tegra_otg_data), GFP_KERNEL);
    if (!tegra)
        return -ENOMEM;

    tegra->otg.dev = &pdev->dev;
    tegra->otg.label = "tegra-otg";
    tegra->otg.state = OTG_STATE_UNDEFINED;
    tegra->otg.set_host = tegra_otg_set_host;
    tegra->otg.set_peripheral = tegra_otg_set_peripheral;
    tegra->otg.set_suspend = tegra_otg_set_suspend;
    tegra->otg.set_power = tegra_otg_set_power;
    spin_lock_init(&tegra->lock);
    wake_lock_init(&tegra->wake_lock, WAKE_LOCK_SUSPEND, "tegra_otg");

    platform_set_drvdata(pdev, tegra);
    tegra_clone = tegra;

    tegra->clk = clk_get(&pdev->dev, NULL);
    if (IS_ERR(tegra->clk)) {
        dev_err(&pdev->dev, "Can't get otg clock\n");
        err = PTR_ERR(tegra->clk);
        goto err_clk;
    }

    /* Enable the clock and leave it enabled */
    err = clk_enable(tegra->clk);
    if (err)
        goto err_clken;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "Failed to get I/O memory\n");
        err = -ENXIO;
        goto err_io;
    }
    tegra->regs = ioremap(res->start, resource_size(res));
    if (!tegra->regs) {
        err = -ENOMEM;
        goto err_io;
    }

    /* We start in a suspended device mode */
    tegra->otg.state = OTG_STATE_A_SUSPEND;

    val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
    val &= ~TEGRA_ID_WAKEUP_EN;							/* Disable wakeup on CableID change */
    val |=  TEGRA_VBUS_WAKEUP_EN; 						/* Enable Phy wakeup on VBUS change */
    val |=  TEGRA_ID_SW_ENABLE;							/* Enable CableID sw overrride */
    val &= ~TEGRA_VBUS_SW_ENABLE;						/* Disable VBUS   sw overrride */
    val |=  TEGRA_ID_SW_VALUE;							/* CableID = 1, for peripheral mode */
    val &= ~TEGRA_VBUS_SW_VALUE;						/* Clean VBUS sw value */
    val |=  TEGRA_VBUS_INT_EN;							/* Enable VBUS change interrupt */
    val &= ~TEGRA_ID_INT_EN;							/* Disable cable ID change interrupt */
    otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);

    err = otg_set_transceiver(&tegra->otg);
    if (err) {
        dev_err(&pdev->dev, "can't register transceiver (%d)\n", err);
        goto err_otg;
    }

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (!res) {
        dev_err(&pdev->dev, "Failed to get IRQ\n");
        err = -ENXIO;
        goto err_irq;
    }
    tegra->irq = res->start;
    err = request_threaded_irq(tegra->irq, tegra_otg_irq,
                               NULL,
                               IRQF_SHARED, "tegra-otg", tegra);
    if (err) {
        dev_err(&pdev->dev, "Failed to register IRQ\n");
        goto err_irq;
    }

    INIT_WORK (&tegra->work, tegra_otg_work);

#ifdef CONFIG_USB_HOTPLUG
    /* Delay a bit toensure registers are updated */
    udelay(1);

    clk_disable(tegra->clk);
#endif

    dev_info(&pdev->dev, "otg transceiver registered\n");
    return 0;

err_irq:
    otg_set_transceiver(NULL);
err_otg:
    iounmap(tegra->regs);
err_io:
    clk_disable(tegra->clk);
err_clken:
    clk_put(tegra->clk);
err_clk:
    wake_lock_destroy(&tegra->wake_lock);
    platform_set_drvdata(pdev, NULL);
    kfree(tegra);
    return err;
}
Пример #18
0
/* Switch interface from host to slave and viceversa */
void tegra_otg_set_host_mode(bool host_mode)
{
    unsigned long val;
    unsigned long flags;

    struct tegra_otg_data *tegra = tegra_clone;
    if (!tegra)
        return;

    dev_info(tegra->otg.dev, "%s: mode: %s", __func__, host_mode ? "host" : "gadget");

    if (tegra->host_mode == host_mode)
        return;

    /* Store the new mode */
    tegra->host_mode = host_mode;

    if (host_mode) {

        /* No interrupts can be used in host mode */
        clk_enable(tegra->clk);
        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; 				/* Do NOT ack pending interrupts */
        val &= ~TEGRA_ID_SW_VALUE;			/* CableID is 0 */
        val &= ~TEGRA_VBUS_INT_EN;			/* We can't use VBUS ints in host mode */
        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

        /* Delay a bit toensure registers are updated */
        udelay(1);

        clk_disable(tegra->clk);

        /* Schedule the switch to host mode */
        if (tegra->event != USB_EVENT_ID) {
            tegra->event = USB_EVENT_ID;
            schedule_work(&tegra->work);
        }

    } else {

        /* Enable the VBUS id interrupts, so we will know when they disconnected */
        clk_enable(tegra->clk);
        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; /* Do NOT ack pending interrupts */
        val |=  TEGRA_ID_SW_VALUE;			/* CableID is 1 */
        val |=  TEGRA_VBUS_INT_EN;			/* Use VBUS ints in suspend mode to wakeup */
        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

        /* Delay a bit toensure registers are updated */
        udelay(1);

        clk_disable(tegra->clk);

        /* Leave some time for VBUS stabilization */
        mdelay(10);

        /* Check if we are in device mode with something plugged to us */
        tegra_otg_check_status();
    }
}
Пример #19
0
static void tegra_otg_work(struct work_struct *work)
{
    struct tegra_otg_data *tegra = container_of(work, struct tegra_otg_data, work);
    struct otg_transceiver *otg = &tegra->otg;
    enum usb_otg_state from = otg->state;
    enum usb_otg_state to = OTG_STATE_UNDEFINED;
    unsigned long event = tegra->event;

    unsigned long val;
    unsigned long flags;

    if (event == USB_EVENT_VBUS)
        to = OTG_STATE_B_PERIPHERAL;
    else if (event == USB_EVENT_ID)
        to = OTG_STATE_A_HOST;
    else
        to = OTG_STATE_A_SUSPEND;

    if (from == to)
        return;

    otg->state = to;

    dev_info(tegra->otg.dev, "%s --> %s", tegra_state_name(from),
             tegra_state_name(to));
    dev_info(tegra->otg.dev, "host: %p, gadget: %p", otg->host, otg->gadget);

    clk_enable(tegra->clk);

    if ((to == OTG_STATE_A_HOST) && (from == OTG_STATE_B_PERIPHERAL)
            && otg->gadget) {

        usb_gadget_vbus_disconnect(otg->gadget);
        wake_unlock(&tegra->wake_lock);

        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; /* Do NOT ack pending interrupts */

        val &= ~TEGRA_ID_SW_VALUE;			/* CableID is 0 */
        val &= ~TEGRA_VBUS_INT_EN;			/* We can't use VBUS ints in host mode */

        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

        tegra_start_host(tegra);

    } else if ((to == OTG_STATE_A_HOST) && (from == OTG_STATE_A_SUSPEND)) {

        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; /* Do NOT ack pending interrupts */

        val &= ~TEGRA_ID_SW_VALUE;			/* CableID is 0 */
        val &= ~TEGRA_VBUS_INT_EN;			/* We can't use VBUS ints in host mode */

        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

        tegra_start_host(tegra);

    } else if ((to == OTG_STATE_A_SUSPEND) && (from == OTG_STATE_A_HOST)) {

        tegra_stop_host(tegra);

        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; /* Do NOT ack pending interrupts */

        val |=  TEGRA_ID_SW_VALUE;			/* CableID is 1 */
        val |=  TEGRA_VBUS_INT_EN;			/* Use VBUS ints in suspend mode to wakeup */

        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

    } else if ((to == OTG_STATE_B_PERIPHERAL) && (from == OTG_STATE_A_HOST)
               && otg->gadget) {

        tegra_stop_host(tegra);

        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; /* Do NOT ack pending interrupts */

        val |=  TEGRA_ID_SW_VALUE;			/* CableID is 1 */
        val |=  TEGRA_VBUS_INT_EN;			/* Use VBUS ints in suspend mode to wakeup */

        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

        wake_lock(&tegra->wake_lock);
        usb_gadget_vbus_connect(otg->gadget);

    } else if ((to == OTG_STATE_B_PERIPHERAL) && (from == OTG_STATE_A_SUSPEND)
               && otg->gadget) {

        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; /* Do NOT ack pending interrupts */

        val |=  TEGRA_ID_SW_VALUE;			/* CableID is 1 */
        val |=  TEGRA_VBUS_INT_EN;			/* Use VBUS ints in suspend mode to wakeup */

        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

        wake_lock(&tegra->wake_lock);
        usb_gadget_vbus_connect(otg->gadget);

    } else if ((to == OTG_STATE_A_SUSPEND) && (from == OTG_STATE_B_PERIPHERAL)
               && otg->gadget) {

        usb_gadget_vbus_disconnect(otg->gadget);
        wake_unlock(&tegra->wake_lock);

        spin_lock_irqsave(&tegra->lock, flags);
        val = otg_readl(tegra, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        val &= ~TEGRA_INTS; /* Do NOT ack pending interrupts */

        val |=  TEGRA_ID_SW_VALUE;			/* CableID is 1 */
        val |=  TEGRA_VBUS_INT_EN;			/* Use VBUS ints in suspend mode to wakeup */

        otg_writel(tegra, val, TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
        spin_unlock_irqrestore(&tegra->lock, flags);

    }

    /* Delay a bit toensure registers are updated */
    udelay(1);

    clk_disable(tegra->clk);
}