Ejemplo n.º 1
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;
		}
	}
}
Ejemplo n.º 2
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) {
				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;
		}
	}
}