static int usb_lpm_enter(struct usb_hcd *hcd) { unsigned long flags; struct device *dev = container_of((void *)hcd, struct device, driver_data); struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd); spin_lock_irqsave(&mhcd->lock, flags); if (mhcd->in_lpm) { pr_info("%s: already in lpm. nothing to do\n", __func__); spin_unlock_irqrestore(&mhcd->lock, flags); return 0; } if (HC_IS_RUNNING(hcd->state)) { pr_info("%s: can't enter into lpm. controller is runnning\n", __func__); spin_unlock_irqrestore(&mhcd->lock, flags); return -1; } pr_info("%s: lpm enter procedure started\n", __func__); mhcd->in_lpm = 1; disable_irq(hcd->irq); spin_unlock_irqrestore(&mhcd->lock, flags); if (usb_suspend_phy(hcd)) { mhcd->in_lpm = 0; enable_irq(hcd->irq); pr_info("phy suspend failed\n"); pr_info("%s: lpm enter procedure end\n", __func__); return -1; } msm_xusb_disable_clks(mhcd); if (mhcd->xceiv && mhcd->xceiv->set_suspend) mhcd->xceiv->set_suspend(1); if (device_may_wakeup(dev)) enable_irq_wake(hcd->irq); enable_irq(hcd->irq); pr_info("%s: lpm enter procedure end\n", __func__); return 0; }
static void usb_do_work(struct work_struct *w) { struct usb_info *ui = container_of(w, struct usb_info, work); unsigned long iflags; unsigned flags, _vbus; for (;;) { spin_lock_irqsave(&ui->lock, iflags); flags = ui->flags; ui->flags = 0; _vbus = vbus; spin_unlock_irqrestore(&ui->lock, iflags); /* give up if we have nothing to do */ if (flags == 0) break; switch (ui->state) { case USB_STATE_IDLE: if (flags & USB_FLAG_START) { pr_info("msm72k_udc: IDLE -> ONLINE\n"); clk_set_rate(ui->ebi1clk, 128000000); udelay(10); if (ui->coreclk) clk_enable(ui->coreclk); clk_enable(ui->clk); clk_enable(ui->pclk); if (ui->otgclk) clk_enable(ui->otgclk); usb_reset(ui); ui->state = USB_STATE_ONLINE; usb_do_work_check_vbus(ui); } break; case USB_STATE_ONLINE: /* If at any point when we were online, we received * the signal to go offline, we must honor it */ if (flags & USB_FLAG_VBUS_OFFLINE) { pr_info("msm72k_udc: ONLINE -> OFFLINE\n"); /* synchronize with irq context */ spin_lock_irqsave(&ui->lock, iflags); ui->running = 0; ui->online = 0; msm72k_pullup(&ui->gadget, 0); spin_unlock_irqrestore(&ui->lock, iflags); if (ui->usb_connected) ui->usb_connected(0); /* terminate any transactions, etc */ flush_all_endpoints(ui); if (ui->driver) { printk(KERN_INFO "usb: notify offline\n"); ui->driver->disconnect(&ui->gadget); } usb_phy_reset(ui); /* power down phy, clock down usb */ spin_lock_irqsave(&ui->lock, iflags); usb_suspend_phy(ui); clk_disable(ui->pclk); clk_disable(ui->clk); if (ui->otgclk) clk_disable(ui->otgclk); if (ui->coreclk) clk_disable(ui->coreclk); clk_set_rate(ui->ebi1clk, 0); spin_unlock_irqrestore(&ui->lock, iflags); ui->state = USB_STATE_OFFLINE; usb_do_work_check_vbus(ui); break; } if (flags & USB_FLAG_RESET) { pr_info("msm72k_udc: ONLINE -> RESET\n"); usb_reset(ui); pr_info("msm72k_udc: RESET -> ONLINE\n"); break; } break; case USB_STATE_OFFLINE: /* If we were signaled to go online and vbus is still * present when we received the signal, go online. */ if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) { pr_info("msm72k_udc: OFFLINE -> ONLINE\n"); clk_set_rate(ui->ebi1clk, 128000000); udelay(10); if (ui->coreclk) clk_enable(ui->coreclk); clk_enable(ui->clk); clk_enable(ui->pclk); if (ui->otgclk) clk_enable(ui->otgclk); usb_reset(ui); /* detect shorted D+/D-, indicating AC power */ msleep(10); if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) if (ui->usb_connected) ui->usb_connected(2); ui->state = USB_STATE_ONLINE; usb_do_work_check_vbus(ui); } break; } } }