static irqreturn_t usb_interrupt(int irq, void *data) { struct usb_info *ui = data; unsigned n; n = readl(USB_USBSTS); writel(n, USB_USBSTS); /* somehow we got an IRQ while in the reset sequence: ignore it */ if (ui->running == 0) return IRQ_HANDLED; if (n & STS_PCI) { switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) { case PORTSC_PSPD_FS: INFO("msm72k_udc: portchange USB_SPEED_FULL\n"); ui->gadget.speed = USB_SPEED_FULL; break; case PORTSC_PSPD_LS: INFO("msm72k_udc: portchange USB_SPEED_LOW\n"); ui->gadget.speed = USB_SPEED_LOW; break; case PORTSC_PSPD_HS: INFO("msm72k_udc: portchange USB_SPEED_HIGH\n"); ui->gadget.speed = USB_SPEED_HIGH; break; } } if (n & STS_URI) { INFO("msm72k_udc: reset\n"); writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT); writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE); writel(0xffffffff, USB_ENDPTFLUSH); writel(0, USB_ENDPTCTRL(1)); if (ui->online != 0) { /* marking us offline will cause ept queue attempts ** to fail */ ui->online = 0; flush_all_endpoints(ui); /* XXX: we can't seem to detect going offline, * XXX: so deconfigure on reset for the time being */ if (ui->driver) { printk(KERN_INFO "usb: notify offline\n"); ui->driver->disconnect(&ui->gadget); } } } if (n & STS_SLI) INFO("msm72k_udc: suspend\n"); if (n & STS_UI) { n = readl(USB_ENDPTSETUPSTAT); if (n & EPT_RX(0)) handle_setup(ui); n = readl(USB_ENDPTCOMPLETE); writel(n, USB_ENDPTCOMPLETE); while (n) { unsigned bit = __ffs(n); handle_endpoint(ui, bit); n = n & (~(1 << bit)); } } return IRQ_HANDLED; }
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; unsigned cfg_val; 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 */ cfg_val = ulpi_read(ui, ULPI_CONFIG_REG); cfg_val |= ULPI_AMPLITUDE_MAX; ulpi_write(ui, cfg_val, 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); spin_lock_irqsave(&ui->lock, flags); ui->running = 1; spin_unlock_irqrestore(&ui->lock, flags); }
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; } } }