示例#1
0
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;
}
示例#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) {
				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;
		}
	}
}
示例#3
0
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);
}
示例#4
0
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);
}
示例#5
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;
		}
	}
}