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; }
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(); }
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; }
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; }
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__); }
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__); }
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(®_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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
/* 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(); } }
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); }