void usb_function_reenumerate(void) { struct usb_info *ui = the_usb_info; /* disable and re-enable the D+ pullup */ msm72k_pullup(&ui->gadget, false); msleep(10); msm72k_pullup(&ui->gadget, true); }
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { struct usb_info *dev = the_usb_info; if (!dev) return -ENODEV; if (!driver || driver != dev->driver || !driver->unbind) return -EINVAL; msm72k_pullup(&dev->gadget, 0); dev->state = USB_STATE_IDLE; dev->online = 0; device_remove_file(&dev->gadget.dev, &dev_attr_wakeup); device_remove_file(&dev->gadget.dev, &dev_attr_usb_state); device_remove_file(&dev->gadget.dev, &dev_attr_usb_speed); device_remove_file(&dev->gadget.dev, &dev_attr_chg_type); device_remove_file(&dev->gadget.dev, &dev_attr_chg_current); driver->disconnect(&dev->gadget); driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; device_del(&dev->gadget.dev); dev_dbg(&dev->pdev->dev, "unregistered gadget driver '%s'\n", driver->driver.name); 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; } } }
static void usb_reset(struct usb_info *ui) { unsigned long flags; printk(KERN_INFO "hsusb: reset controller\n"); spin_lock_irqsave(&ui->lock, flags); ui->running = 0; spin_unlock_irqrestore(&ui->lock, flags); /* To prevent phantom packets being received by the usb core on * some devices, put the controller into reset prior to * resetting the phy. */ writel(2, USB_USBCMD); msleep(10); #if 0 /* we should flush and shutdown cleanly if already running */ writel(0xffffffff, USB_ENDPTFLUSH); msleep(2); #endif if (usb_phy_reset(ui) < 0) pr_err("%s: Phy reset failed!\n", __func__); msleep(100); /* toggle non-driving mode after phy reset to ensure that * we cause a disconnect event to the host */ ulpi_write(ui, 0x18, 0x6); msleep(1); ulpi_write(ui, 0x8, 0x5); msleep(1); /* RESET */ writel(2, USB_USBCMD); msleep(10); #ifdef CONFIG_ARCH_MSM7X00A /* INCR4 BURST mode */ writel(0x01, USB_SBUSCFG); #else /* bursts of unspecified length. */ writel(0, USB_AHBBURST); /* Use the AHB transactor */ writel(0, USB_AHBMODE); #endif /* select DEVICE mode */ writel(0x12, USB_USBMODE); msleep(1); /* select ULPI phy */ writel(0x80000000, USB_PORTSC); ulpi_init(ui); writel(ui->dma, USB_ENDPOINTLISTADDR); configure_endpoints(ui); /* marking us offline will cause ept queue attempts to fail */ ui->online = 0; /* terminate any pending transactions */ flush_all_endpoints(ui); if (ui->driver) { printk(KERN_INFO "usb: notify offline\n"); ui->driver->disconnect(&ui->gadget); } /* enable interrupts */ writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); /* go to RUN mode (D+ pullup enable) */ msm72k_pullup(&ui->gadget, 1); spin_lock_irqsave(&ui->lock, flags); ui->running = 1; spin_unlock_irqrestore(&ui->lock, flags); }
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) { int ret; struct msm_otg *otg = to_msm_otg(ui->xceiv); if (!_vbus) { ui->state = USB_STATE_OFFLINE; break; } msm72k_pm_qos_update(1); pr_info("msm72k_udc: IDLE -> ONLINE\n"); usb_reset(ui); ret = request_irq(otg->irq, usb_interrupt, IRQF_SHARED, ui->pdev->name, ui); /* FIXME: should we call BUG_ON when * requst irq fails */ if (ret) { pr_err("hsusb: peripheral: request irq" " failed:(%d)", ret); msm72k_pm_qos_update(0); break; } ui->irq = otg->irq; msm72k_pullup(&ui->gadget, 1); if (ui->chg_connected) { msleep(500); ui->chg_type = is_wall_charger(ui); ui->chg_current = ui->chg_type ? 1500 : 100; ui->chg_connected(ui->chg_type); } 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"); otg_set_suspend(ui->xceiv, 0); /* 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->chg_connected) { ui->chg_type = CHG_TYPE_INVALID; ui->chg_current = 0; ui->chg_connected(ui->chg_type); } if (ui->irq) { free_irq(ui->irq, ui); ui->irq = 0; } 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); } /* power down phy, clock down usb */ otg_set_suspend(ui->xceiv, 1); ui->state = USB_STATE_OFFLINE; usb_do_work_check_vbus(ui); msm72k_pm_qos_update(0); break; } if (flags & USB_FLAG_SUSPEND) { /* TBD: Not supporting bus suspend */ ui->chg_vbus_draw(0); break; } if (flags & USB_FLAG_CONFIGURED) { ui->chg_current = ui->b_max_pow; ui->chg_vbus_draw(ui->b_max_pow); break; } if (flags & USB_FLAG_RESET) { pr_info("msm72k_udc: ONLINE -> RESET\n"); msm72k_pullup(&ui->gadget, 0); usb_reset(ui); msm72k_pullup(&ui->gadget, 1); 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) { int ret; struct msm_otg *otg = to_msm_otg(ui->xceiv); pr_info("msm72k_udc: OFFLINE -> ONLINE\n"); msm72k_pm_qos_update(1); otg_set_suspend(ui->xceiv, 0); usb_reset(ui); if (ui->usb_connected) ui->usb_connected(1); ui->state = USB_STATE_ONLINE; usb_do_work_check_vbus(ui); ret = request_irq(otg->irq, usb_interrupt, IRQF_SHARED, ui->pdev->name, ui); /* FIXME: should we call BUG_ON when * requst irq fails */ if (ret) { pr_err("hsusb: peripheral: request irq" " failed:(%d)", ret); break; } ui->irq = otg->irq; enable_irq_wake(otg->irq); msm72k_pullup(&ui->gadget, 1); if (ui->chg_connected) { msleep(500); ui->chg_type = is_wall_charger(ui); ui->chg_current = ui->chg_type ? 1500 : 100; ui->chg_connected(ui->chg_type); } } break; } } }
static void usb_reset(struct usb_info *ui) { unsigned long flags; unsigned otgsc; INFO("msm72k_udc: reset controller\n"); spin_lock_irqsave(&ui->lock, flags); ui->running = 0; spin_unlock_irqrestore(&ui->lock, flags); #if 0 /* we should flush and shutdown cleanly if already running */ writel(0xffffffff, USB_ENDPTFLUSH); msleep(2); #endif otgsc = readl(USB_OTGSC); /* RESET */ writel(2, USB_USBCMD); msleep(10); if (ui->phy_reset) ui->phy_reset(); /* select DEVICE mode */ writel(0x12, USB_USBMODE); msleep(1); /* select ULPI phy */ writel(0x80000000, USB_PORTSC); /* electrical compliance failure in eye-diagram tests * were observed w/ integrated phy. To avoid failure * raise signal amplitude to 400mv */ ulpi_write(ui, ULPI_AMPLITUDE, ULPI_CONFIG_REG); /* fix potential usb stability issues with "integrated phy" * by enabling unspecified length of INCR burst and using * the AHB master interface of the AHB2AHB transactor */ writel(0, USB_AHB_BURST); writel(0, USB_AHB_MODE); ulpi_init(ui); writel(ui->dma, USB_ENDPOINTLISTADDR); configure_endpoints(ui); /* marking us offline will cause ept queue attempts to fail */ ui->online = 0; /* terminate any pending transactions */ flush_all_endpoints(ui); if (ui->driver) { printk(KERN_INFO "usb: notify offline\n"); ui->driver->disconnect(&ui->gadget); } /* enable interrupts */ writel(otgsc, USB_OTGSC); writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); /* go to RUN mode (D+ pullup enable) */ msm72k_pullup(&ui->gadget, 1); spin_lock_irqsave(&ui->lock, flags); ui->running = 1; spin_unlock_irqrestore(&ui->lock, flags); }