{
unsigned int tmp;
+ pr_debug("%s\n", __func__);
+
+ /* if we're in OTG mode, and the Host is currently using the port,
+ * stop now and don't rip the controller out from under the
+ * ehci driver
+ */
+ if (udc->gadget.is_otg) {
+ if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
+ pr_debug("udc: Leaving early\n");
+ return;
+ }
+ }
+
/* disable all INTR */
fsl_writel(0, &dr_regs->usbintr);
{
u32 speed;
+ if (udc->bus_reset)
+ udc->bus_reset = 0;
+
/* Bus resetting is finished */
if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
/* Get the speed */
if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
VDBG("Bus reset");
+ /* Bus is reseting */
+ udc->bus_reset = 1;
/* Reset all the queues, include XD, dTD, EP queue
* head and TR Queue */
reset_queues(udc);
/* Reset Received */
if (irq_src & USB_STS_RESET) {
+ VDBG("reset int");
reset_irq(udc);
status = IRQ_HANDLED;
}
goto out;
}
- /* Enable DR IRQ reg and Set usbcmd reg Run bit */
- dr_controller_run(udc_controller);
- udc_controller->usb_state = USB_STATE_ATTACHED;
- udc_controller->ep0_state = WAIT_FOR_SETUP;
- udc_controller->ep0_dir = 0;
+ if (udc_controller->transceiver) {
+ /* Suspend the controller until OTG enable it */
+ udc_controller->stopped = 1;
+ printk(KERN_INFO "Suspend udc for OTG auto detect\n");
+
+ /* connect to bus through transceiver */
+ if (udc_controller->transceiver) {
+ retval = otg_set_peripheral(udc_controller->transceiver,
+ &udc_controller->gadget);
+ if (retval < 0) {
+ ERR("can't bind to transceiver\n");
+ driver->unbind(&udc_controller->gadget);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+ return retval;
+ }
+ }
+ } else {
+ /* Enable DR IRQ reg and set USBCMD reg Run bit */
+ dr_controller_run(udc_controller);
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_state = WAIT_FOR_SETUP;
+ udc_controller->ep0_dir = 0;
+ }
printk(KERN_INFO "%s: bind to driver %s\n",
udc_controller->gadget.name, driver->driver.name);
spin_lock_init(&udc_controller->lock);
udc_controller->stopped = 1;
+#ifdef CONFIG_USB_OTG
+ if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+ udc_controller->transceiver = otg_get_transceiver();
+ if (!udc_controller->transceiver) {
+ ERR("Can't find OTG driver!\n");
+ ret = -ENODEV;
+ goto err_kfree;
+ }
+ }
+#endif
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENXIO;
goto err_kfree;
}
- if (!request_mem_region(res->start, res->end - res->start + 1,
- driver_name)) {
- ERR("request mem region for %s failed\n", pdev->name);
- ret = -EBUSY;
- goto err_kfree;
+ if (pdata->operating_mode == FSL_USB2_DR_DEVICE) {
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ driver_name)) {
+ ERR("request mem region for %s failed\n", pdev->name);
+ ret = -EBUSY;
+ goto err_kfree;
+ }
}
dr_regs = ioremap(res->start, resource_size(res));
goto err_free_irq;
}
- /* initialize usb hw reg except for regs for EP,
- * leave usbintr reg untouched */
- dr_controller_setup(udc_controller);
+ if (!udc_controller->transceiver) {
+ /* initialize usb hw reg except for regs for EP,
+ * leave usbintr reg untouched */
+ dr_controller_setup(udc_controller);
+ }
fsl_udc_clk_finalize(pdev);
if (ret < 0)
goto err_free_irq;
+ if (udc_controller->transceiver)
+ udc_controller->gadget.is_otg = 1;
+
/* setup QH and epctrl for ep0 */
ep0_setup(udc_controller);
err_iounmap_noclk:
iounmap(dr_regs);
err_release_mem_region:
- release_mem_region(res->start, res->end - res->start + 1);
+ if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
+ release_mem_region(res->start, res->end - res->start + 1);
err_kfree:
kfree(udc_controller);
udc_controller = NULL;
dma_pool_destroy(udc_controller->td_pool);
free_irq(udc_controller->irq, udc_controller);
iounmap(dr_regs);
- release_mem_region(res->start, res->end - res->start + 1);
+ if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
+ release_mem_region(res->start, res->end - res->start + 1);
device_unregister(&udc_controller->gadget.dev);
/* free udc --wait for the release() finished */
return 0;
}
+static int fsl_udc_otg_suspend(struct device *dev, pm_message_t state)
+{
+ struct fsl_udc *udc = udc_controller;
+ u32 mode, usbcmd;
+
+ mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK;
+
+ pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped);
+
+ /*
+ * If the controller is already stopped, then this must be a
+ * PM suspend. Remember this fact, so that we will leave the
+ * controller stopped at PM resume time.
+ */
+ if (udc->stopped) {
+ pr_debug("gadget already stopped, leaving early\n");
+ udc->already_stopped = 1;
+ return 0;
+ }
+
+ if (mode != USB_MODE_CTRL_MODE_DEVICE) {
+ pr_debug("gadget not in device mode, leaving early\n");
+ return 0;
+ }
+
+ /* stop the controller */
+ usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP;
+ fsl_writel(usbcmd, &dr_regs->usbcmd);
+
+ udc->stopped = 1;
+
+ pr_info("USB Gadget suspended\n");
+
+ return 0;
+}
+
+static int fsl_udc_otg_resume(struct device *dev)
+{
+ pr_debug("%s(): stopped %d already_stopped %d\n", __func__,
+ udc_controller->stopped, udc_controller->already_stopped);
+
+ /*
+ * If the controller was stopped at suspend time, then
+ * don't resume it now.
+ */
+ if (udc_controller->already_stopped) {
+ udc_controller->already_stopped = 0;
+ pr_debug("gadget was already stopped, leaving early\n");
+ return 0;
+ }
+
+ pr_info("USB Gadget resume\n");
+
+ return fsl_udc_resume(NULL);
+}
+
/*-------------------------------------------------------------------------
Register entry point for the peripheral controller driver
--------------------------------------------------------------------------*/
.driver = {
.name = (char *)driver_name,
.owner = THIS_MODULE,
+ /* udc suspend/resume called from OTG driver */
+ .suspend = fsl_udc_otg_suspend,
+ .resume = fsl_udc_otg_resume,
},
};
pdata->regs = hcd->regs;
+ if (pdata->power_budget)
+ hcd->power_budget = pdata->power_budget;
+
/*
* do platform specific init: check the clock, grab/config pins, etc.
*/
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
goto err4;
+
+#ifdef CONFIG_USB_OTG
+ if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ ehci->transceiver = otg_get_transceiver();
+ dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n",
+ hcd, ehci, ehci->transceiver);
+
+ if (ehci->transceiver) {
+ retval = otg_set_host(ehci->transceiver,
+ &ehci_to_hcd(ehci)->self);
+ if (retval) {
+ if (ehci->transceiver)
+ put_device(ehci->transceiver->dev);
+ goto err4;
+ }
+ } else {
+ dev_err(&pdev->dev, "can't find transceiver\n");
+ retval = -ENODEV;
+ goto err4;
+ }
+ }
+#endif
return retval;
err4:
struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ if (ehci->transceiver) {
+ otg_set_host(ehci->transceiver, NULL);
+ put_device(ehci->transceiver->dev);
+ }
usb_remove_hcd(hcd);
#define EHCI_FSL_PM_OPS NULL
#endif /* CONFIG_PM */
+#ifdef CONFIG_USB_OTG
+static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 status;
+
+ if (!port)
+ return -EINVAL;
+
+ port--;
+
+ /* start port reset before HNP protocol time out */
+ status = readl(&ehci->regs->port_status[port]);
+ if (!(status & PORT_CONNECT))
+ return -ENODEV;
+
+ /* khubd will finish the reset later */
+ if (ehci_is_TDI(ehci)) {
+ writel(PORT_RESET |
+ (status & ~(PORT_CSC | PORT_PEC | PORT_OCC)),
+ &ehci->regs->port_status[port]);
+ } else {
+ writel(PORT_RESET, &ehci->regs->port_status[port]);
+ }
+
+ return 0;
+}
+#else
+#define ehci_start_port_reset NULL
+#endif /* CONFIG_USB_OTG */
+
+
static const struct hc_driver ehci_fsl_hc_driver = {
.description = hcd_name,
.product_desc = "Freescale On-Chip EHCI Host Controller",
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
+ .start_port_reset = ehci_start_port_reset,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,