diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/generic.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/arcotg_udc.c | 514 | ||||
-rw-r--r-- | drivers/usb/gadget/arcotg_udc.h | 12 | ||||
-rw-r--r-- | drivers/usb/gadget/composite.c | 39 | ||||
-rw-r--r-- | drivers/usb/gadget/f_acm.c | 9 | ||||
-rw-r--r-- | drivers/usb/gadget/file_storage.c | 9 | ||||
-rw-r--r-- | drivers/usb/gadget/fsl_updater.c | 79 | ||||
-rw-r--r-- | drivers/usb/gadget/fsl_updater.h | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-arc.c | 280 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 1 | ||||
-rw-r--r-- | drivers/usb/otg/fsl_otg.c | 233 |
17 files changed, 826 insertions, 438 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 2bfc41ece0e1..b8134ad5f05f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -59,6 +59,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> +#include <linux/serial.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> @@ -609,6 +610,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) acm->throttle = 0; tasklet_schedule(&acm->urb_task); + set_bit(ASYNCB_INITIALIZED, &acm->port.flags); rv = tty_port_block_til_ready(&acm->port, tty, filp); done: mutex_unlock(&acm->mutex); diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 69e5773abfce..07f503aa9078 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1754,6 +1754,9 @@ int usb_resume(struct device *dev, pm_message_t msg) udev = to_usb_device(dev); +/* At otg mode, if it is a device wakeup interrupt, the host should do nothing */ + if (udev->bus->is_b_host) + return 0; /* If udev->skip_sys_resume is set then udev was already suspended * when the system sleep started, so we don't want to resume it * during this system wakeup. @@ -1765,7 +1768,7 @@ int usb_resume(struct device *dev, pm_message_t msg) /* Avoid PM error messages for devices disconnected while suspended * as we'll display regular disconnect messages just a bit later. */ - if (status == -ENODEV) + if (status == -ENODEV || status == -ESHUTDOWN) return 0; return status; } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 66e8a424c9f4..539a2c0dde00 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -196,7 +196,6 @@ extern void usb_host_set_wakeup(struct device *wkup_dev, bool para); static int generic_suspend(struct usb_device *udev, pm_message_t msg) { int rc; - u32 temp; /* Normal USB devices suspend through their upstream port. * Root hubs don't have upstream ports to suspend, @@ -204,25 +203,7 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) * interfaces manually by doing a bus (or "global") suspend. */ if (!udev->parent) { - struct usb_hcd *hcd = - container_of(udev->bus, struct usb_hcd, self); - struct fsl_usb2_platform_data *pdata; - pdata = hcd->self.controller->platform_data; - rc = hcd_bus_suspend(udev, msg); - - if (device_may_wakeup(hcd->self.controller)) { - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - /* enable remote wake up irq */ - usb_host_set_wakeup(hcd->self.controller, true); - - /* Put PHY into low power mode */ - temp = readl(hcd->regs + 0x184); - writel(temp | (1 << 23), (hcd->regs + 0x184)); - - if (pdata->usb_clock_for_pm) - pdata->usb_clock_for_pm(false); - } /* Non-root devices don't need to do anything for FREEZE or PRETHAW */ } else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW) @@ -236,7 +217,6 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) static int generic_resume(struct usb_device *udev, pm_message_t msg) { int rc; - u32 temp; /* Normal USB devices resume/reset through their upstream port. * Root hubs don't have upstream ports to resume or reset, @@ -244,13 +224,6 @@ static int generic_resume(struct usb_device *udev, pm_message_t msg) * interfaces manually by doing a bus (or "global") resume. */ if (!udev->parent) { - struct usb_hcd *hcd = - container_of(udev->bus, struct usb_hcd, self); - - if (device_may_wakeup(hcd->self.controller)) { - temp = readl(hcd->regs + 0x184); - writel(temp & (~(1 << 23)), (hcd->regs + 0x184)); - } rc = hcd_bus_resume(udev, msg); } else rc = usb_port_resume(udev, msg); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index d27ad104731c..2f47bdc7c93a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1739,6 +1739,7 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) int status; int old_state = hcd->state; + printk("%s\n", __func__); dev_dbg(&rhdev->dev, "bus %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend"); if (!hcd->driver->bus_suspend) { @@ -1876,7 +1877,6 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum); irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd *hcd = __hcd; - struct fsl_usb2_platform_data *pdata; unsigned long flags; irqreturn_t rc; @@ -1885,25 +1885,14 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) * assume it's never used. */ local_irq_save(flags); - - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { - /* Need open clock for register access */ - pdata = hcd->self.controller->platform_data; - if (pdata->usb_clock_for_pm) - pdata->usb_clock_for_pm(true); - - /* if receive a remote wakeup interrrupt after suspend */ - if (usb_host_wakeup_irq(hcd->self.controller)) { - /* disable remote wake up irq */ - usb_host_set_wakeup(hcd->self.controller, false); - - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - hcd->driver->irq(hcd); - rc = IRQ_HANDLED; - } else + /* At otg mode, the host does need to handle device interrupt */ + if (hcd->self.is_b_host){ + local_irq_restore(flags); + return IRQ_NONE; + } + else if (unlikely(hcd->state == HC_STATE_HALT || + !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { rc = IRQ_NONE; - } else if (unlikely(hcd->state == HC_STATE_HALT)) { - rc = IRQ_NONE; } else if (hcd->driver->irq(hcd) == IRQ_NONE) { rc = IRQ_NONE; } else { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d47201c75915..cc8f911afbc4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1177,12 +1177,6 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) "Unsupported bus topology: hub nested too deep\n"); return -E2BIG; } -#ifdef CONFIG_PM - /* Defaultly disable autosuspend for hub and reley on sys - * to enable it. - */ - hdev->autosuspend_disabled = 1; -#endif #ifdef CONFIG_USB_OTG_BLACKLIST_HUB if (hdev->parent) { @@ -2304,7 +2298,6 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; - /* fail if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { struct usb_device *udev; @@ -2328,8 +2321,15 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) static int hub_resume(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); + struct usb_hcd *hcd = bus_to_hcd(hub->hdev->bus); dev_dbg(&intf->dev, "%s\n", __func__); + /* At otg mode, if the hcd which the hub is attached to is not accessible, + * It should do nothing. + */ + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + return 0; + hub_activate(hub, HUB_RESUME); return 0; } diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ce44379bd172..c29ebda61b2f 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -466,7 +466,6 @@ config USB_GOKU config USB_GADGET_ARC boolean "Freescale USB Device Controller" depends on ARCH_MXC || ARCH_STMP3XXX || ARCH_MXS - depends on !USB_EHCI_ARC_OTG select USB_GADGET_DUALSPEED select USB_OTG_UTILS select USB_GADGET_DUALSPEED if USB_GADGET_FSL_1504 || USB_GADGET_FSL_UTMI @@ -792,6 +791,15 @@ config USB_G_PRINTER For more information, see Documentation/usb/gadget_printer.txt which includes sample code for accessing the device file. +config USB_ANDROID + tristate "Android Gadget" + depends on SWITCH + help + The Android gadget provides mass storage and adb transport. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_android". + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 477114e43372..545c0e256e28 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -41,6 +41,7 @@ gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o g_printer-objs := printer.o g_cdc-objs := cdc2.o +g_android-objs := android.o f_adb.o f_mass_storage.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_AUDIO) += g_audio.o @@ -51,4 +52,5 @@ obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o +obj-$(CONFIG_USB_ANDROID) += g_android.o diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c index 1577c93c35bb..8e94549f891e 100644 --- a/drivers/usb/gadget/arcotg_udc.c +++ b/drivers/usb/gadget/arcotg_udc.c @@ -106,19 +106,6 @@ extern struct resource *otg_get_resources(void); extern void fsl_platform_set_test_mode(struct fsl_usb2_platform_data *pdata, enum usb_test_mode mode); -static inline void -dr_wake_up_enable(struct fsl_udc *udc, bool enable) -{ - struct fsl_usb2_platform_data *pdata; - pdata = udc->pdata; - - if (enable && (!device_may_wakeup(udc_controller->gadget.dev.parent))) - return; - - if (pdata->wake_up_enable) - pdata->wake_up_enable(pdata, enable); -} - #ifdef CONFIG_WORKAROUND_ARCUSB_REG_RW static void safe_writel(u32 val32, void *addr) { @@ -177,6 +164,25 @@ static inline void dump_ep_queue(struct fsl_ep *ep) } #endif +#if (defined CONFIG_ARCH_MX35 || defined CONFIG_ARCH_MX25) +/* + * The Phy at MX35 and MX25 have bugs, it must disable, and re-eable phy + * if the phy clock is disabled before + */ +static void reset_phy(void) +{ + u32 phyctrl; + phyctrl = fsl_readl(&dr_regs->phyctrl1); + phyctrl &= ~PHY_CTRL0_USBEN; + fsl_writel(phyctrl, &dr_regs->phyctrl1); + + phyctrl = fsl_readl(&dr_regs->phyctrl1); + phyctrl |= PHY_CTRL0_USBEN; + fsl_writel(phyctrl, &dr_regs->phyctrl1); +} +#else +static void reset_phy(void){; } +#endif /*----------------------------------------------------------------- * done() - retire a request; caller blocked irqs * @status : request status to be set, only works when @@ -264,9 +270,12 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) static void nuke(struct fsl_ep *ep, int status) { ep->stopped = 1; - - /* Flush fifo */ - fsl_ep_fifo_flush(&ep->ep); + /* + * At udc stop mode, the clock is already off + * So flush fifo, should be done at clock on mode. + */ + if (!ep->udc->stopped) + fsl_ep_fifo_flush(&ep->ep); /* Whether this eq has request linked */ while (!list_empty(&ep->queue)) { @@ -281,31 +290,85 @@ static void nuke(struct fsl_ep *ep, int status) /*------------------------------------------------------------------ Internal Hardware related function ------------------------------------------------------------------*/ +static inline void +dr_wake_up_enable(struct fsl_udc *udc, bool enable) +{ + struct fsl_usb2_platform_data *pdata; + pdata = udc->pdata; -static void dr_phy_low_power_mode(struct fsl_udc *udc, bool enable) + if (pdata && pdata->wake_up_enable) + pdata->wake_up_enable(pdata, enable); +} +static bool clk_stoped = false; +static inline void dr_clk_gate(bool on) { - u32 temp; + struct fsl_usb2_platform_data *pdata = udc_controller->pdata; - if (!device_may_wakeup(udc_controller->gadget.dev.parent)) + if (!pdata || !pdata->usb_clock_for_pm) return; + if (on && clk_stoped) { + pdata->usb_clock_for_pm(true); + clk_stoped = false; + } + if (!on && !clk_stoped) { + pdata->usb_clock_for_pm(false); + clk_stoped = true; + } + if (on) + reset_phy(); +} - if (enable) { - temp = fsl_readl(&dr_regs->portsc1); - temp |= PORTSCX_PHY_LOW_POWER_SPD; - fsl_writel(temp, &dr_regs->portsc1); +static void dr_phy_low_power_mode(struct fsl_udc *udc, bool enable) +{ + struct fsl_usb2_platform_data *pdata = udc->pdata; + u32 portsc; - if (udc_controller->pdata->usb_clock_for_pm) - udc_controller->pdata->usb_clock_for_pm(false); + if (pdata && pdata->phy_lowpower_suspend) { + pdata->phy_lowpower_suspend(enable); } else { - if (udc_controller->pdata->usb_clock_for_pm) - udc_controller->pdata->usb_clock_for_pm(true); - - temp = fsl_readl(&dr_regs->portsc1); - temp &= ~PORTSCX_PHY_LOW_POWER_SPD; - fsl_writel(temp, &dr_regs->portsc1); + if (enable){ + portsc = fsl_readl(&dr_regs->portsc1); + portsc |= PORTSCX_PHY_LOW_POWER_SPD; + fsl_writel(portsc, &dr_regs->portsc1); + } else { + portsc = fsl_readl(&dr_regs->portsc1); + portsc &= ~PORTSCX_PHY_LOW_POWER_SPD; + fsl_writel(portsc, &dr_regs->portsc1); + } } } + +/* workaroud for some boards, maybe there is a large capacitor between the ground and the Vbus + * that will cause the vbus dropping very slowly when device is detached, + * may cost 2-3 seconds to below 0.8V */ +static void udc_wait_b_session_low(void) +{ + u32 temp; + u32 wait = 5000; /* max wait time is 5000 ms */ + /* if we are in host mode, don't need to care the B session */ + if ((fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) == 0) + return; + /* if the udc is dettached , there will be a suspend irq */ + if (udc_controller->usb_state != USB_STATE_SUSPENDED) + return; + temp = fsl_readl(&dr_regs->otgsc); + temp &= ~(OTGSC_B_SESSION_VALID_IRQ_EN ); + fsl_writel(temp, &dr_regs->otgsc); + + do { + if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_B_SESSION_VALID)) + break; + mdelay(1); + wait -= 1; + } while(wait); + if (!wait) + printk("ERROR!!!!!: the vbus can not be lower then 0.8V for 5 seconds, Pls Check your HW design\n"); + temp = fsl_readl(&dr_regs->otgsc); + temp |= (OTGSC_B_SESSION_VALID_IRQ_EN ); + fsl_writel(temp, &dr_regs->otgsc); +} + static int dr_controller_setup(struct fsl_udc *udc) { unsigned int tmp = 0, portctrl = 0; @@ -427,37 +490,36 @@ static void dr_controller_run(struct fsl_udc *udc) fsl_writel(temp, &dr_regs->usbintr); - if (device_may_wakeup(udc_controller->gadget.dev.parent)) { - /* enable BSV irq */ - temp = fsl_readl(&dr_regs->otgsc); - temp |= OTGSC_B_SESSION_VALID_IRQ_EN; - fsl_writel(temp, &dr_regs->otgsc); - } + /* enable BSV irq */ + temp = fsl_readl(&dr_regs->otgsc); + temp |= OTGSC_B_SESSION_VALID_IRQ_EN; + fsl_writel(temp, &dr_regs->otgsc); /* If vbus not on and used low power mode */ - if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_B_SESSION_VALID) - && device_may_wakeup(udc_controller->gadget.dev.parent)) { - /* enable wake up */ - dr_wake_up_enable(udc, true); + if (!(temp & OTGSC_B_SESSION_VALID)) { /* Set stopped before low power mode */ udc->stopped = 1; - /* close PHY clock */ + /* enable wake up */ + dr_wake_up_enable(udc, true); + /* enter lower power mode */ dr_phy_low_power_mode(udc, true); - printk(KERN_INFO "udc enter low power mode \n"); + printk(KERN_INFO "%s: udc enter low power mode \n", __func__); } else { +#ifdef CONFIG_ARCH_MX37 /* add some delay for USB timing issue. USB may be recognize as FS device during USB gadget remote wake up function */ mdelay(100); +#endif /* Clear stopped bit */ udc->stopped = 0; - /* Set controller to Run */ + + /* The usb line has already been connected to pc */ temp = fsl_readl(&dr_regs->usbcmd); temp |= USB_CMD_RUN_STOP; fsl_writel(temp, &dr_regs->usbcmd); - printk(KERN_INFO "udc run \n"); } return; @@ -680,7 +742,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, case USB_ENDPOINT_XFER_ISOC: /* Calculate transactions needed for high bandwidth iso */ mult = (unsigned char)(1 + ((max >> 11) & 0x03)); - max = max & 0x8ff; /* bit 0~10 */ + max = max & 0x7ff; /* bit 0~10 */ /* 3 transactions at most */ if (mult > 3) goto en_done; @@ -1953,15 +2015,36 @@ static void suspend_irq(struct fsl_udc *udc) udc->driver->suspend(&udc->gadget); } -/* Process Wake up interrupt */ -static void wake_up_irq(struct fsl_udc *udc) -{ - pr_debug("%s\n", __func__); - - /* disable wake up irq */ - dr_wake_up_enable(udc_controller, false); - - udc->stopped = 0; +/* Process Wake up interrupt + * Be careful that some boards will use ID pin to control the VBUS on/off + * in these case, after the device enter the lowpower mode(clk off, + * phy lowpower mode, wakeup enable), then an udisk is attaced to the otg port, + * there will be an Vbus wakeup event and then an ID change wakeup, But the Vbus + * event is not expected, so there is an workaround that will detect the ID, if ID=0 + * we just need the ID event so we can not disable the wakeup + * + * false: host wakeup event + * true: device wakeup event +*/ +static bool wake_up_irq(struct fsl_udc *udc) +{ + /* Because the IC design needs to remove the glitch on ID so the otgsc bit 8 will + * be delayed max 2 ms to show the real ID pin value + */ + mdelay(3); + + /* if the ID=0, let arc host process the wakeup */ + if (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) { + dr_wake_up_enable(udc_controller, false); + dr_phy_low_power_mode(udc, false); + printk("device wake up event\n"); + return true; + }else {/* wakeup is vbus wake event, but not for device so we need to clear b session */ + int irq_src = fsl_readl(&dr_regs->otgsc) & (~OTGSC_ID_CHANGE_IRQ_STS); + fsl_writel(irq_src, &dr_regs->otgsc); + printk("The host wakeup event, should be handled by host\n"); + return false; + } } static void bus_resume(struct fsl_udc *udc) @@ -2018,72 +2101,60 @@ static void reset_irq(struct fsl_udc *udc) /* Write 1s to the flush register */ fsl_writel(0xffffffff, &dr_regs->endptflush); - 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); - udc->usb_state = USB_STATE_DEFAULT; - } else { - VDBG("Controller reset"); - /* initialize usb hw reg except for regs for EP, not - * touch usbintr reg */ - dr_controller_setup(udc); - - /* Reset all internal used Queues */ - reset_queues(udc); - - ep0_setup(udc); - - /* Enable DR IRQ reg, Set Run bit, change udc state */ - dr_controller_run(udc); - udc->usb_state = USB_STATE_ATTACHED; - } + /* Bus is reseting */ + udc->bus_reset = 1; + /* Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + reset_queues(udc); + udc->usb_state = USB_STATE_DEFAULT; } /* if wakup udc, return true; else return false*/ bool try_wake_up_udc(struct fsl_udc *udc) { u32 irq_src; + bool b_device; /* when udc is stopped, only handle wake up irq */ if (udc->stopped) { - if (!device_may_wakeup(&(udc->pdata->pdev->dev))) - return false; - - dr_phy_low_power_mode(udc_controller, false); - /* check to see if wake up irq */ irq_src = fsl_readl(&dr_regs->usbctrl); if (irq_src & USB_CTRL_OTG_WUIR) { - wake_up_irq(udc); - } else { - dr_phy_low_power_mode(udc_controller, true); + if (wake_up_irq(udc) == false){ + return false; /* host wakeup event */ + } } } - if (!device_may_wakeup(udc_controller->gadget.dev.parent)) - return true; - /* check if Vbus change irq */ irq_src = fsl_readl(&dr_regs->otgsc); if (irq_src & OTGSC_B_SESSION_VALID_IRQ_STS) { u32 tmp; + /* Because the IC design needs to remove the glitch on ID so the otgsc bit 8 will + * be delayed max 2 ms to show the real ID pin value, as it needs to use ID to judge + * host or device + */ + mdelay(3); + b_device = (irq_src & OTGSC_STS_USB_ID)? true:false; fsl_writel(irq_src, &dr_regs->otgsc); + if (!b_device) + return false; tmp = fsl_readl(&dr_regs->usbcmd); /* check BSV bit to see if fall or rise */ if (irq_src & OTGSC_B_SESSION_VALID) { + if (udc->suspended) /*let the system pm resume the udc */ + return true; udc->stopped = 0; fsl_writel(tmp | USB_CMD_RUN_STOP, &dr_regs->usbcmd); - printk(KERN_INFO "udc out low power mode\n"); + printk(KERN_INFO "%s: udc out low power mode\n", __func__); } else { - printk(KERN_INFO "udc enter low power mode \n"); + printk(KERN_INFO "%s: udc enter low power mode \n", __func__); + if (udc->driver) + udc->driver->disconnect(&udc->gadget); fsl_writel(tmp & ~USB_CMD_RUN_STOP, &dr_regs->usbcmd); + udc->stopped = 1; /* enable wake up */ dr_wake_up_enable(udc, true); - udc->stopped = 1; /* close USB PHY clock */ dr_phy_low_power_mode(udc, true); return false; @@ -2092,7 +2163,6 @@ bool try_wake_up_udc(struct fsl_udc *udc) return true; } - /* * USB device controller interrupt handler */ @@ -2103,15 +2173,29 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) irqreturn_t status = IRQ_NONE; unsigned long flags; - if (try_wake_up_udc(udc) == false) - return IRQ_NONE; - spin_lock_irqsave(&udc->lock, flags); + if (udc->stopped) + dr_clk_gate(true); + + if (try_wake_up_udc(udc) == false) { + goto irq_end; + } +#ifdef CONFIG_USB_OTG + /* if no gadget register in this driver, we need do noting */ + if (udc->transceiver->gadget == NULL) + goto irq_end; + + /* only handle device interrupt event */ + if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) { + goto irq_end; + } +#endif + irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr); /* Clear notification bits */ fsl_writel(irq_src, &dr_regs->usbsts); - /* VDBG("irq_src [0x%8x]", irq_src); */ + VDBG("0x%x\n", irq_src); /* Need to resume? */ if (udc->usb_state == USB_STATE_SUSPENDED) @@ -2156,12 +2240,24 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Sleep Enable (Suspend) */ if (irq_src & USB_STS_SUSPEND) { + VDBG("suspend int"); suspend_irq(udc); status = IRQ_HANDLED; } if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { - VDBG("Error IRQ %x ", irq_src); + printk(KERN_ERR "Error IRQ %x ", irq_src); + if (irq_src & USB_STS_SYS_ERR) { + printk(KERN_ERR "This error can't be recoveried, \ + please reboot your board\n"); + printk(KERN_ERR "If this error happens frequently, \ + please check your dma buffer\n"); + } + } + +irq_end: + if (udc->stopped){ + dr_clk_gate(false); } spin_unlock_irqrestore(&udc->lock, flags); @@ -2176,9 +2272,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) { int retval = -ENODEV; unsigned long flags = 0; -#ifndef CONFIG_USB_OTG - u32 portsc; -#endif if (!udc_controller) return -ENODEV; @@ -2196,18 +2289,19 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) spin_lock_irqsave(&udc_controller->lock, flags); driver->driver.bus = 0; + udc_controller->pdata->port_enables = 1; /* hook up the driver */ udc_controller->driver = driver; udc_controller->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc_controller->lock, flags); -#ifndef CONFIG_USB_OTG - if (udc_controller->pdata->usb_clock_for_pm) - udc_controller->pdata->usb_clock_for_pm(true); + dr_clk_gate(true); + /* It doesn't need to switch usb from low power mode to normal mode + * at otg mode + */ + if (!udc_controller->transceiver){ + dr_phy_low_power_mode(udc_controller, false); + } - portsc = fsl_readl(&dr_regs->portsc1); - portsc &= ~PORTSCX_PHY_LOW_POWER_SPD; - fsl_writel(portsc, &dr_regs->portsc1); -#endif /* bind udc driver to gadget driver */ retval = driver->bind(&udc_controller->gadget); if (retval) { @@ -2219,30 +2313,30 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (udc_controller->transceiver) { /* Suspend the controller until OTG enable it */ - udc_controller->stopped = 1; + udc_controller->suspended = 1;/* let the otg resume it */ printk(KERN_INFO "Suspend udc for OTG auto detect\n"); dr_wake_up_enable(udc_controller, true); - dr_phy_low_power_mode(udc_controller, true); /* export udc suspend/resume call to OTG */ udc_controller->gadget.dev.driver->suspend = (dev_sus)fsl_udc_suspend; udc_controller->gadget.dev.driver->resume = (dev_res)fsl_udc_resume; /* 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; - } + 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; } + //dr_clk_gate(false); } else { /* Enable DR IRQ reg and Set usbcmd reg Run bit */ dr_controller_run(udc_controller); + if (udc_controller->stopped) + dr_clk_gate(false); udc_controller->usb_state = USB_STATE_ATTACHED; udc_controller->ep0_dir = 0; } @@ -2250,8 +2344,10 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) udc_controller->gadget.name, driver->driver.name); out: - if (retval) + if (retval){ printk(KERN_DEBUG "retval %d \n", retval); + udc_controller->pdata->port_enables = 0; + } return retval; } EXPORT_SYMBOL(usb_gadget_register_driver); @@ -2261,7 +2357,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { struct fsl_ep *loop_ep; unsigned long flags; - u32 portsc; if (!udc_controller) return -ENODEV; @@ -2269,15 +2364,16 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) if (!driver || driver != udc_controller->driver || !driver->unbind) return -EINVAL; + if(udc_controller->stopped) + dr_clk_gate(true); + if (udc_controller->transceiver) (void)otg_set_peripheral(udc_controller->transceiver, 0); - /* open phy clock for following operation */ - dr_phy_low_power_mode(udc_controller, false); - /* stop DR, disable intr */ dr_controller_stop(udc_controller); + udc_controller->pdata->port_enables = 0; /* in fact, no needed */ udc_controller->usb_state = USB_STATE_ATTACHED; udc_controller->ep0_dir = 0; @@ -2299,14 +2395,11 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) udc_controller->gadget.dev.driver = 0; udc_controller->driver = 0; - dr_wake_up_enable(udc_controller, false); - - portsc = fsl_readl(&dr_regs->portsc1); - portsc |= PORTSCX_PHY_LOW_POWER_SPD; - fsl_writel(portsc, &dr_regs->portsc1); + if (udc_controller->gadget.is_otg) { + dr_wake_up_enable(udc_controller, true); + } - if (udc_controller->pdata->usb_clock_for_pm) - udc_controller->pdata->usb_clock_for_pm(false); + dr_phy_low_power_mode(udc_controller, true); printk(KERN_INFO "unregistered gadget driver '%s'\r\n", driver->driver.name); @@ -2705,14 +2798,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) int ret = -ENODEV; unsigned int i; u32 dccparams; -#ifndef CONFIG_USB_OTG - u32 portsc; -#endif - - if (strcmp(pdev->name, driver_name)) { - VDBG("Wrong device\n"); - return -ENODEV; - } udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); if (udc_controller == NULL) { @@ -2729,6 +2814,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) ret = -ENODEV; goto err1a; } + udc_controller->gadget.is_otg = 1; #endif if ((pdev->dev.parent) && @@ -2768,6 +2854,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err2a; } + /* Due to mx35/mx25's phy's bug */ + reset_phy(); + if (pdata->have_sysif_regs) usb_sys_regs = (struct usb_sys_interface *) ((u32)dr_regs + USB_DR_SYS_OFFSET); @@ -2826,12 +2915,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) if (ret < 0) goto err3; - if (udc_controller->transceiver) { - udc_controller->gadget.is_otg = 1; - /* now didn't support lpm in OTG mode*/ - device_set_wakeup_capable(&pdev->dev, 0); - } - /* setup QH and epctrl for ep0 */ ep0_setup(udc_controller); @@ -2874,20 +2957,29 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef POSTPONE_FREE_LAST_DTD last_free_td = NULL; #endif + #ifndef CONFIG_USB_OTG /* disable all INTR */ fsl_writel(0, &dr_regs->usbintr); - dr_wake_up_enable(udc_controller, false); +#else + dr_wake_up_enable(udc_controller, true); +#endif + +/* + * As mx25/mx35 does not implement clk_gate, should not let phy to low + * power mode due to IC bug + */ +#if !(defined CONFIG_ARCH_MX35 || defined CONFIG_ARCH_MX25) +{ + dr_phy_low_power_mode(udc_controller, true); +} +#endif udc_controller->stopped = 1; - portsc = fsl_readl(&dr_regs->portsc1); - portsc |= PORTSCX_PHY_LOW_POWER_SPD; - fsl_writel(portsc, &dr_regs->portsc1); + /* let the gadget register function open the clk */ + dr_clk_gate(false); - if (udc_controller->pdata->usb_clock_for_pm) - udc_controller->pdata->usb_clock_for_pm(false); -#endif create_proc_file(); return 0; @@ -2914,9 +3006,6 @@ err1a: */ static int __exit fsl_udc_remove(struct platform_device *pdev) { -#ifndef CONFIG_USB_OTG - struct resource *res; -#endif struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; DECLARE_COMPLETION(done); @@ -2925,7 +3014,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) return -ENODEV; udc_controller->done = &done; /* open USB PHY clock */ - dr_phy_low_power_mode(udc_controller, false); + if (udc_controller->stopped) + dr_clk_gate(true); /* DR has been stopped in usb_gadget_unregister_driver() */ remove_proc_file(); @@ -2948,8 +3038,11 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) iounmap((u8 __iomem *)dr_regs); #ifndef CONFIG_USB_OTG +{ + struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); +} #endif device_unregister(&udc_controller->gadget.dev); @@ -2963,6 +3056,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) if (pdata->platform_uninit) pdata->platform_uninit(pdata); + if (udc_controller->stopped) + dr_clk_gate(false); return 0; } @@ -2970,10 +3065,19 @@ static int udc_suspend(struct fsl_udc *udc) { u32 mode, usbcmd; - /* open clock for register access */ - if (udc_controller->pdata->usb_clock_for_pm) - udc_controller->pdata->usb_clock_for_pm(true); - + /* + * When it is the PM suspend routine and the device has no + * abilities to wakeup system, it should not set wakeup enable. + * Otherwise, the system will wakeup even the user only wants to + * charge using usb + */ + if (udc_controller->gadget.dev.parent->power.status + == DPM_SUSPENDING) { + if (!device_may_wakeup(udc_controller->gadget.dev.parent)) + dr_wake_up_enable(udc, false); + else + dr_wake_up_enable(udc, true); + } mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK; usbcmd = fsl_readl(&dr_regs->usbcmd); @@ -2984,9 +3088,8 @@ static int udc_suspend(struct fsl_udc *udc) * PM suspend. Remember this fact, so that we will leave the * controller stopped at PM resume time. */ - if (udc->stopped) { + if (udc->suspended) { pr_debug("gadget already stopped, leaving early\n"); - udc->already_stopped = 1; goto out; } @@ -2995,22 +3098,30 @@ static int udc_suspend(struct fsl_udc *udc) goto out; } + /* For some buggy hardware designs, see comment of this function for detail */ + udc_wait_b_session_low(); + udc->stopped = 1; - /* if the suspend is not for switch to host in otg mode */ - if ((!(udc->gadget.is_otg)) || - (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) { - dr_wake_up_enable(udc, true); - dr_phy_low_power_mode(udc, true); - } /* stop the controller */ usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP; fsl_writel(usbcmd, &dr_regs->usbcmd); + /* if the suspend is not for switch to host in otg mode */ + if ((!(udc->gadget.is_otg)) || + (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) { + if (device_may_wakeup(udc_controller->gadget.dev.parent)) { + dr_wake_up_enable(udc, true); + } + } + + dr_phy_low_power_mode(udc, true); printk(KERN_INFO "USB Gadget suspended\n"); out: - if (udc_controller->pdata->usb_clock_for_pm) - udc_controller->pdata->usb_clock_for_pm(false); + udc->suspended++; + if (udc->suspended > 2) + printk("ERROR: suspended times > 2\n"); + return 0; } @@ -3020,13 +3131,24 @@ out: -----------------------------------------------------------------*/ static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state) { + int ret; +#ifdef CONFIG_USB_OTG + if (udc_controller->transceiver->gadget == NULL) + return 0; +#endif + if (udc_controller->stopped) + dr_clk_gate(true); if (((!(udc_controller->gadget.is_otg)) || (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) && (udc_controller->usb_state > USB_STATE_POWERED) && - (udc_controller->usb_state < USB_STATE_SUSPENDED)) - return -EBUSY; + (udc_controller->usb_state < USB_STATE_SUSPENDED)) { + return -EBUSY;/* keep the clk on */ + } + else + ret = udc_suspend(udc_controller); + dr_clk_gate(false); - return udc_suspend(udc_controller); + return ret; } /*----------------------------------------------------------------- @@ -3035,30 +3157,54 @@ static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state) *-----------------------------------------------------------------*/ static int fsl_udc_resume(struct platform_device *pdev) { - pr_debug("%s(): stopped %d already_stopped %d\n", __func__, - udc_controller->stopped, udc_controller->already_stopped); - + pr_debug("%s(): stopped %d suspended %d\n", __func__, + udc_controller->stopped, udc_controller->suspended); + printk("udc resume\n"); +#ifdef CONFIG_USB_OTG + if (udc_controller->transceiver->gadget == NULL) + return 0; +#endif + if (udc_controller->stopped) + dr_clk_gate(true); /* * 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; - } + /* + * If it is PM resume routine, the udc is at low power mode, + * and the udc has no abilities to wakeup system, it should + * set the abilities to wakeup itself. Otherwise, the usb + * subsystem will not leave from low power mode. + */ + if (!device_may_wakeup(udc_controller->gadget.dev.parent) && + udc_controller->gadget.dev.parent->power.status + == DPM_RESUMING){ + dr_wake_up_enable(udc_controller, true); + } + if (--udc_controller->suspended) { + printk("gadget was already stopped, leaving early\n"); + goto out; + } /* Enable DR irq reg and set controller Run */ if (udc_controller->stopped) { + /* if in host mode, we need to do nothing */ + if ((fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID) == 0) { + goto out; + } dr_wake_up_enable(udc_controller, false); dr_phy_low_power_mode(udc_controller, false); - mdelay(1); - + mdelay(10); dr_controller_setup(udc_controller); dr_controller_run(udc_controller); } udc_controller->usb_state = USB_STATE_ATTACHED; udc_controller->ep0_dir = 0; +out: + /* if udc is resume by otg id change and no device + * connecting to the otg, otg will enter low power mode*/ + if (udc_controller->stopped) + dr_clk_gate(false); printk(KERN_INFO "USB Gadget resumed\n"); return 0; diff --git a/drivers/usb/gadget/arcotg_udc.h b/drivers/usb/gadget/arcotg_udc.h index 480d953dcf58..8d344acb8fef 100644 --- a/drivers/usb/gadget/arcotg_udc.h +++ b/drivers/usb/gadget/arcotg_udc.h @@ -266,6 +266,7 @@ struct usb_sys_interface { #define PORTSCX_SPEED_BIT_POS (26) /* OTGSC Register Bit Masks */ +#define OTGSC_ID_CHANGE_IRQ_STS (1 << 16) #define OTGSC_B_SESSION_VALID_IRQ_EN (1 << 27) #define OTGSC_B_SESSION_VALID_IRQ_STS (1 << 19) #define OTGSC_B_SESSION_VALID (1 << 11) @@ -365,6 +366,7 @@ struct usb_sys_interface { /* PHY control0 Register Bit Masks */ #define PHY_CTRL0_CONF2 (1 << 26) +#define PHY_CTRL0_USBEN (1 << 24) /* USB UTMI PHY Enable */ /* USB UH2 CTRL Register Bits */ #define USB_UH2_OVBWK_EN (1 << 6) /* OTG VBUS Wakeup Enable */ @@ -592,9 +594,15 @@ struct fsl_udc { struct otg_transceiver *transceiver; unsigned softconnect:1; unsigned vbus_active:1; - unsigned stopped:1; unsigned remote_wakeup:1; - unsigned already_stopped:1; + /* we must distinguish the stopped and suspended state, + * stopped means the udc enter lowpower mode, suspended + * means the udc is suspended by system pm or by otg + * switching to host mode.if the udc in suspended state + * it also in the stopped state, while if the udc in + * stopped state,it may not be in the suspended state*/ + unsigned stopped:1; + int suspended; struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ struct fsl_req *status_req; /* ep0 status request */ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 59e85234fa0a..2e79b8c389a4 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -236,6 +236,7 @@ static int config_buf(struct usb_configuration *config, int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; struct usb_function *f; int status; + int interfaceCount = 0; /* write the config descriptor */ c = buf; @@ -266,8 +267,16 @@ static int config_buf(struct usb_configuration *config, descriptors = f->hs_descriptors; else descriptors = f->descriptors; - if (!descriptors) + if (f->hidden || !descriptors || descriptors[0] == NULL) { + for (; f != config->interface[interfaceCount];) { + interfaceCount++; + c->bNumInterfaces--; + } continue; + } + for (; f != config->interface[interfaceCount];) + interfaceCount++; + status = usb_descriptor_fillbuf(next, len, (const struct usb_descriptor_header **) descriptors); if (status < 0) @@ -756,11 +765,11 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) goto unknown; - if (cdev->config) + if (cdev->config) { *(u8 *)req->buf = cdev->config->bConfigurationValue; - else + value = min(w_length, (u16) 1); + } else *(u8 *)req->buf = 0; - value = min(w_length, (u16) 1); break; /* function drivers must handle get/set altsetting; if there's @@ -810,6 +819,9 @@ unknown: */ if ((ctrl->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { + if (cdev->config == NULL) + return value; + f = cdev->config->interface[intf]; if (f && f->setup) value = f->setup(f, ctrl); @@ -824,6 +836,25 @@ unknown: value = c->setup(c, ctrl); } + /* If the vendor request is not processed (value < 0), + * call all device registered configure setup callbacks + * to process it. + * This is used to handle the following cases: + * - vendor request is for the device and arrives before + * setconfiguration. + * - Some devices are required to handle vendor request before + * setconfiguration such as MTP, USBNET. + */ + + if (value < 0) { + struct usb_configuration *cfg; + + list_for_each_entry(cfg, &cdev->configs, list) { + if (cfg && cfg->setup) + value = cfg->setup(cfg, ctrl); + } + } + goto done; } diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 7953948bfe4a..7dd1a8bbe382 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -761,3 +761,12 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num) kfree(acm); return status; } + +int __init acm_function_add(struct usb_composite_dev *cdev, + struct usb_configuration *c) +{ + int ret = acm_bind_config(c, 0); + if (ret == 0) + gserial_setup(c->cdev->gadget, 1); + return ret; +} diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 66105ce49672..8b0a13202573 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -728,6 +728,7 @@ struct fsg_dev { #include "fsl_updater.h" #endif +static int do_set_interface(struct fsg_dev *fsg, int altsetting); typedef void (*fsg_routine_t)(struct fsg_dev *); static int exception_in_progress(struct fsg_dev *fsg) @@ -1108,6 +1109,14 @@ static void fsg_disconnect(struct usb_gadget *gadget) struct fsg_dev *fsg = get_gadget_data(gadget); DBG(fsg, "disconnect or port reset\n"); + /* + * The disconnect exception will call do_set_config, and therefore will + * visit controller registers. However it is a delayed event, and will be + * handled at another process, so the controller maybe have already close the + * usb clock.*/ + if (fsg->new_config) + do_set_interface(fsg, -1);/* disable the interface */ + raise_exception(fsg, FSG_STATE_DISCONNECT); } diff --git a/drivers/usb/gadget/fsl_updater.c b/drivers/usb/gadget/fsl_updater.c index 8b4b54f8cca7..50acce441a90 100644 --- a/drivers/usb/gadget/fsl_updater.c +++ b/drivers/usb/gadget/fsl_updater.c @@ -29,6 +29,7 @@ static int utp_init(struct fsg_dev *fsg) INIT_LIST_HEAD(&utp_context.write); mutex_init(&utp_context.lock); + /* the max message is 64KB */ utp_context.buffer = vmalloc(0x10000); if (!utp_context.buffer) return -EIO; @@ -63,6 +64,7 @@ static void utp_user_data_free(struct utp_user_data *uud) kfree(uud); } +/* Get the number of element for list */ static u32 count_list(struct list_head *l) { u32 count = 0; @@ -74,10 +76,11 @@ static u32 count_list(struct list_head *l) return count; } - +/* The routine will not go on if utp_context.queue is empty */ #define WAIT_ACTIVITY(queue) \ wait_event_interruptible(utp_context.wq, !list_empty(&utp_context.queue)) +/* Called by userspace program (uuc) */ static ssize_t utp_file_read(struct file *file, char __user *buf, size_t size, @@ -109,12 +112,15 @@ static ssize_t utp_file_read(struct file *file, "need to put %d\n", size, size_to_put); } + /* + * The user program has already finished data process, + * go on getting data from the host + */ wake_up(&utp_context.list_full_wq); return size_to_put; } - static ssize_t utp_file_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { @@ -127,11 +133,13 @@ static ssize_t utp_file_write(struct file *file, const char __user *buf, return -EACCES; mutex_lock(&utp_context.lock); list_add_tail(&uud->link, &utp_context.write); + /* Go on EXEC routine process */ wake_up(&utp_context.wq); mutex_unlock(&utp_context.lock); return size; } +/* Will be called when the host wants to get the sense data */ static int utp_get_sense(struct fsg_dev *fsg) { if (UTP_CTX(fsg)->processed == 0) @@ -186,6 +194,7 @@ static int utp_do_read(struct fsg_dev *fsg, void *data, size_t size) /* Perform the read */ pr_info("Copied to %p, %d bytes started from %d\n", bh->buf, amount, size - amount_left); + /* from upt buffer to file_storeage buffer */ memcpy(bh->buf, data + size - amount_left, amount); amount_left -= amount; fsg->residue -= amount; @@ -196,6 +205,7 @@ static int utp_do_read(struct fsg_dev *fsg, void *data, size_t size) /* Send this buffer and go read some more */ bh->inreq->zero = 0; + /* USB Physical transfer: Data from device to host */ start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); @@ -326,8 +336,8 @@ static void utp_poll(struct fsg_dev *fsg) if (uud) { if (uud->data.flags & UTP_FLAG_STATUS) { - pr_debug("%s: exit with status %d\n", __func__, - uud->data.status); + printk(KERN_WARNING "%s: exit with status %d\n", + __func__, uud->data.status); UTP_SS_EXIT(fsg, uud->data.status); } else { pr_debug("%s: pass\n", __func__); @@ -356,11 +366,16 @@ static int utp_exec(struct fsg_dev *fsg, mutex_lock(&ctx->lock); list_add_tail(&uud2r->link, &ctx->read); mutex_unlock(&ctx->lock); + /* wake up the read routine */ wake_up(&ctx->wq); if (command[0] == '!') /* there will be no response */ return 0; + /* + * the user program (uuc) will return utp_message + * and add list to write list + */ WAIT_ACTIVITY(write); mutex_lock(&ctx->lock); @@ -382,21 +397,19 @@ static int utp_exec(struct fsg_dev *fsg, if (uud->data.flags & UTP_FLAG_DATA) { memcpy(ctx->buffer, uud->data.data, uud->data.bufsize); UTP_SS_SIZE(fsg, uud->data.bufsize); - utp_user_data_free(uud); - return 0; - } - - if (uud->data.flags & UTP_FLAG_REPORT_BUSY) { - utp_user_data_free(uud); + } else if (uud->data.flags & UTP_FLAG_REPORT_BUSY) { ctx->counter = 0xFFFF; UTP_SS_BUSY(fsg, ctx->counter); - return 0; + } else if (uud->data.flags & UTP_FLAG_STATUS) { + printk(KERN_WARNING "%s: exit with status %d\n", __func__, + uud->data.status); + UTP_SS_EXIT(fsg, uud->data.status); + } else { + pr_debug("%s: pass\n", __func__); + UTP_SS_PASS(fsg); } - utp_user_data_free(uud); - UTP_SS_PASS(fsg); - - return -1; + return 0; } static int utp_send_status(struct fsg_dev *fsg) @@ -470,16 +483,17 @@ static int utp_handle_message(struct fsg_dev *fsg, case UTP_EXEC: pr_debug("%s: EXEC\n", __func__); data = kzalloc(fsg->data_size, GFP_KERNEL); + /* copy data from usb buffer to utp buffer */ utp_do_write(fsg, data, fsg->data_size); utp_exec(fsg, data, fsg->data_size, param); kfree(data); break; - case UTP_GET: + case UTP_GET: /* data from device to host */ pr_debug("%s: GET, %d bytes\n", __func__, fsg->data_size); r = utp_do_read(fsg, UTP_CTX(fsg)->buffer, fsg->data_size); UTP_SS_PASS(fsg); break; - case UTP_PUT: + case UTP_PUT: /* data from host to device */ pr_debug("%s: PUT, %d bytes\n", __func__, fsg->data_size); uud2r = utp_user_data_alloc(fsg->data_size); uud2r->data.bufsize = fsg->data_size; @@ -490,6 +504,37 @@ static int utp_handle_message(struct fsg_dev *fsg, list_add_tail(&uud2r->link, &UTP_CTX(fsg)->read); mutex_unlock(&UTP_CTX(fsg)->lock); wake_up(&UTP_CTX(fsg)->wq); + /* + * Return PASS or FAIL according to uuc's status + * Please open it if need to check uuc's status + * and use another version uuc + */ +#if 0 + struct utp_user_data *uud = NULL; + struct utp_context *ctx; + WAIT_ACTIVITY(write); + ctx = UTP_CTX(fsg); + mutex_lock(&ctx->lock); + + if (!list_empty(&ctx->write)) + uud = list_first_entry(&ctx->write, + struct utp_user_data, link); + + mutex_unlock(&ctx->lock); + if (uud) { + if (uud->data.flags & UTP_FLAG_STATUS) { + printk(KERN_WARNING "%s: exit with status %d\n", + __func__, uud->data.status); + UTP_SS_EXIT(fsg, uud->data.status); + } else { + pr_debug("%s: pass\n", __func__); + UTP_SS_PASS(fsg); + } + utp_user_data_free(uud); + } else{ + UTP_SS_PASS(fsg); + } +#endif UTP_SS_PASS(fsg); wait_event_interruptible(UTP_CTX(fsg)->list_full_wq, diff --git a/drivers/usb/gadget/fsl_updater.h b/drivers/usb/gadget/fsl_updater.h index 44329a9af58a..70e4defa1a9c 100644 --- a/drivers/usb/gadget/fsl_updater.h +++ b/drivers/usb/gadget/fsl_updater.h @@ -59,6 +59,7 @@ static int utp_handle_message(struct fsg_dev *fsg, #define UTP_SS_BUSY(fsg, r) utp_set_sense(fsg, UTP_REPLY_BUSY, (u64)r) #define UTP_SS_SIZE(fsg, r) utp_set_sense(fsg, UTP_REPLY_SIZE, (u64)r) +/* the structure of utp message which is mapped to 16-byte SCSI CBW's CDB */ #pragma pack(1) struct utp_msg { u8 f0; diff --git a/drivers/usb/host/ehci-arc.c b/drivers/usb/host/ehci-arc.c index 21133fb8e47a..5cfcf169e7c7 100644 --- a/drivers/usb/host/ehci-arc.c +++ b/drivers/usb/host/ehci-arc.c @@ -23,9 +23,28 @@ #include <linux/fsl_devices.h> #include <linux/usb/otg.h> +#include "../core/usb.h" #include "ehci-fsl.h" #include <mach/fsl_usb.h> +extern int usb_host_wakeup_irq(struct device *wkup_dev); +extern void usb_host_set_wakeup(struct device *wkup_dev, bool para); +static void fsl_usb_lowpower_mode(struct fsl_usb2_platform_data *pdata, bool enable) +{ + if (enable){ + if (pdata->phy_lowpower_suspend) + pdata->phy_lowpower_suspend(true); + } else { + if (pdata->phy_lowpower_suspend) + pdata->phy_lowpower_suspend(false); + } +} + +static void fsl_usb_clk_gate(struct fsl_usb2_platform_data *pdata, bool enable) +{ + if (pdata->usb_clock_for_pm) + pdata->usb_clock_for_pm(enable); +} #undef EHCI_PROC_PTC #ifdef EHCI_PROC_PTC /* /proc PORTSC:PTC support */ /* @@ -90,8 +109,39 @@ static int ehci_testmode_init(struct ehci_hcd *ehci) #endif /* /proc PORTSC:PTC support */ -/* configure so an HC device and id are always provided */ -/* always called with process context; sleeping is OK */ +/* + * This irq is used to open the hw access and let usb_hcd_irq process the usb event + * ehci_fsl_pre_irq will be called before usb_hcd_irq + */ +static irqreturn_t ehci_fsl_pre_irq(int irq, void *dev) +{ + struct platform_device *pdev = (struct platform_device *)dev; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct fsl_usb2_platform_data *pdata; + + pdata = hcd->self.controller->platform_data; + + /* if it is an otg module and in b device mode, we need to do noting here */ + if (ehci->transceiver && !ehci->transceiver->default_a) + return IRQ_NONE; + + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + /* Need to open clk for accessing the register */ + fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + /* if receive a remote wakeup interrrupt after suspend */ + if (usb_host_wakeup_irq(hcd->self.controller)) { + printk("host wakeup event happens\n"); + /* disable remote wake up irq */ + usb_host_set_wakeup(hcd->self.controller, false); + fsl_usb_lowpower_mode(pdata, false); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + }else { + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + } + } + return IRQ_NONE; +} /** * usb_hcd_fsl_probe - initialize FSL-based HCDs @@ -182,10 +232,19 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, fsl_platform_set_host_mode(hcd); hcd->power_budget = pdata->power_budget; - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + /* + * The ehci_fsl_pre_irq must be registered before usb_hcd_irq, in that case + * it can be called before usb_hcd_irq when irq occurs + */ + retval = request_irq(irq, ehci_fsl_pre_irq, IRQF_SHARED, + "fsl ehci pre interrupt", (void *)pdev); if (retval != 0) goto err4; + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) + goto err5; + fsl_platform_set_vbus_power(pdata, 1); if (pdata->operating_mode == FSL_USB2_DR_OTG) { @@ -199,7 +258,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, if (!ehci->transceiver) { printk(KERN_ERR "can't find transceiver\n"); retval = -ENODEV; - goto err4; + goto err5; } retval = otg_set_host(ehci->transceiver, &ehci_to_hcd(ehci)->self); @@ -216,7 +275,8 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, fsl_platform_set_ahb_burst(hcd); ehci_testmode_init(hcd_to_ehci(hcd)); return retval; - +err5: + free_irq(irq, (void *)pdev); err4: iounmap(hcd->regs); err3: @@ -231,9 +291,6 @@ err1: return retval; } -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - /** * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs * @dev: USB Host Controller being removed @@ -324,6 +381,59 @@ static int ehci_fsl_reinit(struct ehci_hcd *ehci) return 0; } +static int ehci_fsl_bus_suspend(struct usb_hcd *hcd) +{ + int ret = 0; + struct fsl_usb2_platform_data *pdata; + pdata = hcd->self.controller->platform_data; + pr_debug("%s, %s\n", __func__, pdata->name); + + /* the host is already at low power mode */ + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + return 0; + } + + pr_debug("%s, it is the host mode, %s\n", __func__, pdata->name); + + ehci_bus_suspend(hcd); + + if (pdata->platform_suspend) + pdata->platform_suspend(pdata); + + usb_host_set_wakeup(hcd->self.controller, true); + fsl_usb_lowpower_mode(pdata, true); + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + return ret; +} + +static int ehci_fsl_bus_resume(struct usb_hcd *hcd) +{ + int ret = 0; + struct fsl_usb2_platform_data *pdata; + + pdata = hcd->self.controller->platform_data; + pr_debug("%s, %s\n", __func__, pdata->name); + + /* if it is a remote wakeup, it will open clock and clear PHCD automatically */ + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + usb_host_set_wakeup(hcd->self.controller, false); + fsl_usb_lowpower_mode(pdata, false); + } + + if (pdata->platform_resume) + pdata->platform_resume(pdata); + ret = ehci_bus_resume(hcd); + if (ret) + return ret; + + return ret; +} + + /* called during probe() after chip reset completes */ static int ehci_fsl_setup(struct usb_hcd *hcd) { @@ -396,8 +506,8 @@ static const struct hc_driver ehci_fsl_hc_driver = { */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, + .bus_suspend = ehci_fsl_bus_suspend, + .bus_resume = ehci_fsl_bus_resume, .start_port_reset = ehci_start_port_reset, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, @@ -438,13 +548,36 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev, { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); - u32 tmp, port_status; + struct usb_device *roothub = hcd->self.root_hub; + u32 port_status; struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - if (device_may_wakeup(&(pdev->dev))) { - /* Need open clock for register access */ - if (pdata->usb_clock_for_pm) - pdata->usb_clock_for_pm(true); + /* Only handles OTG mode switch event, system suspend event will be done in bus suspend */ + if (pdev->dev.power.status == DPM_SUSPENDING){ + pr_debug("%s, system pm event \n", __func__); + if (!device_may_wakeup(&(pdev->dev))){ + /* Need open clock for register access */ + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + usb_host_set_wakeup(hcd->self.controller, false); + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + } + return 0; + } + /* only the otg host can go here */ + /* wait for all usb device on the hcd dettached */ + while(roothub->children[0] != NULL) + msleep(1); + if ((pdata->operating_mode != FSL_USB2_MPH_HOST) && (!(hcd->state & HC_STATE_SUSPENDED))) + { + usb_lock_device(roothub); + usb_external_suspend_device(roothub, PMSG_USER_SUSPEND); + usb_unlock_device(roothub); + } + + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); } #ifdef DEBUG @@ -457,27 +590,9 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev, pdata->suspended, pdata->already_suspended, mode, tmp); #endif - /* - * If the controller is already suspended, then this must be a - * PM suspend. Remember this fact, so that we will leave the - * controller suspended at PM resume time. - */ - if (pdata->suspended) { - pr_debug("%s: already suspended, leaving early\n", __func__); - pdata->already_suspended = 1; - goto err1; - } - - pr_debug("%s: suspending...\n", __func__); - printk(KERN_INFO "USB Host suspended\n"); port_status = ehci_readl(ehci, &ehci->regs->port_status[0]); - pdev->dev.power.power_state = PMSG_SUSPEND; - - /* ignore non-host interrupts */ - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - /* save EHCI registers */ pdata->pm_command = ehci_readl(ehci, &ehci->regs->command); pdata->pm_command &= ~CMD_RUN; @@ -496,25 +611,11 @@ static int ehci_fsl_drv_suspend(struct platform_device *pdev, /* clear PHCD bit */ pdata->pm_portsc &= ~PORT_PHCD; - - pdata->suspended = 1; - - if (!device_may_wakeup(&(pdev->dev))) { - /* clear PP to cut power to the port */ - tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); - tmp &= ~PORT_POWER; - ehci_writel(ehci, tmp, &ehci->regs->port_status[0]); - goto err1; - } - - tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); - - if (pdata->platform_suspend) - pdata->platform_suspend(pdata); -err1: - if (device_may_wakeup(&(pdev->dev))) { - if (pdata->usb_clock_for_pm) - pdata->usb_clock_for_pm(false); + if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + //fsl_usb_lowpower_mode(pdata ,true); + //usb_host_set_wakeup(hcd->self.controller, true); + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); } return 0; } @@ -523,47 +624,35 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct usb_device *roothub = hcd->self.root_hub; u32 tmp; struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - - pr_debug("%s('%s'): suspend=%d already_suspended=%d\n", __func__, - pdata->name, pdata->suspended, pdata->already_suspended); - - /* - * If the controller was already suspended at suspend time, - * then don't resume it now. - */ - if (pdata->already_suspended) { - pr_debug("already suspended, leaving early\n"); - pdata->already_suspended = 0; - return 0; - } - - if (!pdata->suspended) { - pr_debug("not suspended, leaving early\n"); + /* Only handles OTG mode switch event */ + if (pdev->dev.power.status == DPM_RESUMING){ + pr_debug("%s, system pm event \n", __func__); + if (hcd->self.is_b_host) { + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + } + usb_host_set_wakeup(hcd->self.controller, true); + + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + fsl_usb_clk_gate(hcd->self.controller->platform_data, false); + } + } return 0; } - - /* If hcd is resumed by non-usb wakeup events, - * then usb clocks are still not open when come here */ - if (device_may_wakeup(&(pdev->dev))) { - /* Need open clock for register access */ - if (pdata->usb_clock_for_pm) - pdata->usb_clock_for_pm(true); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + fsl_usb_clk_gate(hcd->self.controller->platform_data, true); + //usb_host_set_wakeup(hcd->self.controller, false); + //fsl_usb_lowpower_mode(pdata, false); } - tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); - - pdata->suspended = 0; - - pr_debug("%s resuming...\n", __func__); - + printk("USB Host resume ... %s\n", pdata->name); /* set host mode */ fsl_platform_set_host_mode(hcd); - if (pdata->platform_resume) - pdata->platform_resume(pdata); - /* restore EHCI registers */ ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); ehci_writel(ehci, pdata->pm_command, &ehci->regs->command); @@ -575,32 +664,21 @@ static int ehci_fsl_drv_resume(struct platform_device *pdev) ehci_writel(ehci, pdata->pm_configured_flag, &ehci->regs->configured_flag); - /* set bit should be done by wakeup irq routine if may wakeup */ - if (!device_may_wakeup(&(pdev->dev))) - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - else - while (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) - msleep(1); - - pdev->dev.power.power_state = PMSG_ON; tmp = ehci_readl(ehci, &ehci->regs->command); tmp |= CMD_RUN; ehci_writel(ehci, tmp, &ehci->regs->command); - usb_hcd_resume_root_hub(hcd); - - printk(KERN_INFO "USB Host resumed\n"); - - if (device_may_wakeup(&(pdev->dev))) { - if (pdata->usb_clock_for_pm) - pdata->usb_clock_for_pm(false); + if ((hcd->state & HC_STATE_SUSPENDED)){ + usb_lock_device(roothub); + usb_external_resume_device(roothub, PMSG_USER_RESUME); + usb_unlock_device(roothub); } + printk(KERN_INFO "USB Host resume ok\n"); return 0; } -#endif /* CONFIG_USB_OTG */ - +#endif MODULE_ALIAS("platform:fsl-ehci"); static struct platform_driver ehci_fsl_driver = { diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index de459bbd1eb1..44ff32306362 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -112,6 +112,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) int port; int mask; + printk("%s\n", __func__); ehci_dbg(ehci, "suspend root hub\n"); if (time_before (jiffies, ehci->next_statechange)) diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index b1454886fd7a..81a2985632b6 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -34,6 +34,7 @@ #include <linux/init.h> #include <linux/reboot.h> #include <linux/timer.h> +#include <linux/jiffies.h> #include <linux/list.h> #include <linux/usb.h> #include <linux/device.h> @@ -41,6 +42,7 @@ #include <linux/usb/gadget.h> #include <linux/workqueue.h> #include <linux/time.h> +#include <linux/usb/fsl_xcvr.h> #include <linux/fsl_devices.h> #include <linux/platform_device.h> #include <linux/irq.h> @@ -60,6 +62,8 @@ #define DRIVER_DESC "Freescale USB OTG Driver" #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC +#define TIMER_FREQ 1000 /* 100 ms*/ +#define IDLE_TIME 5000 /* 1000 ms */ MODULE_DESCRIPTION("Freescale USB OTG Transceiver Driver"); @@ -89,6 +93,13 @@ static struct fsl_otg_config fsl_otg_initdata = { .otg_port = 1, }; +/* the timer is used to monitor the otg loading, if idle for some times + * we will close the otg clk + */ +static unsigned long last_busy; +static bool clk_stopped; +static struct timer_list monitor_timer; + int write_ulpi(u8 addr, u8 data) { u32 temp; @@ -136,17 +147,10 @@ void fsl_otg_dischrg_vbus(int on) } /* A-device driver vbus, controlled through PP bit in PORTSC */ -void fsl_otg_drv_vbus(int on) +void fsl_otg_drv_vbus(struct fsl_usb2_platform_data *pdata, int on) { -/* if (on) - usb_dr_regs->portsc = - cpu_to_le32((le32_to_cpu(usb_dr_regs->portsc) & - ~PORTSC_W1C_BITS) | PORTSC_PORT_POWER); - else - usb_dr_regs->portsc = - cpu_to_le32(le32_to_cpu(usb_dr_regs->portsc) & - ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER); -*/ + if (pdata->xcvr_ops && pdata->xcvr_ops->set_vbus_power) + pdata->xcvr_ops->set_vbus_power(pdata->xcvr_ops, pdata, on); } /* @@ -395,6 +399,61 @@ int fsl_otg_tick_timer(void) return expired; } +static void fsl_otg_clk_gate(bool on) +{ + struct device *dev = fsl_otg_dev->otg.dev; + struct fsl_usb2_platform_data *pdata; + + if (dev) { + pdata = dev->platform_data; + if (pdata && pdata->usb_clock_for_pm) + pdata->usb_clock_for_pm(on); + } +} + +static void fsl_otg_clk_ctl(void) +{ + if (clk_stopped){ + fsl_otg_clk_gate(true); + clk_stopped = false; + } + last_busy = jiffies; +} + +static void fsl_otg_loading_monitor(unsigned long data) +{ + unsigned long now = jiffies; + if (!clk_stopped){ + if (time_after(now, last_busy + msecs_to_jiffies(IDLE_TIME))){ + printk("otg is idle for some times,so we close the clock %x\n", le32_to_cpu(usb_dr_regs->otgsc)); + clk_stopped = true; + fsl_otg_clk_gate(false); + printk("close otg clk ok\n"); + } + } + mod_timer(&monitor_timer, jiffies + msecs_to_jiffies(TIMER_FREQ)); +} + +/** + * Enable vbus interrupt + * The otg cares USB_ID interrupt + * The device cares B Sesstion Valid + */ +static void b_session_irq_enable(bool enable) +{ + int osc = le32_to_cpu(usb_dr_regs->otgsc); + /* The other interrupts' status should not be cleared */ + osc &= ~(OTGSC_INTSTS_USB_ID | OTGSC_INTSTS_A_VBUS_VALID + | OTGSC_INTSTS_A_SESSION_VALID | OTGSC_INTSTS_B_SESSION_VALID); + osc |= OTGSC_INTSTS_B_SESSION_VALID; + + if (enable) + osc |= OTGSC_INTR_B_SESSION_VALID_EN; + else + osc &= ~OTGSC_INTR_B_SESSION_VALID_EN; + usb_dr_regs->otgsc = cpu_to_le32(osc); +} + /* Reset controller, not reset the bus */ void otg_reset_controller(void) { @@ -438,7 +497,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on) retval = host_pdrv->resume(host_pdev); if (fsm->id) { /* default-b */ - fsl_otg_drv_vbus(1); + fsl_otg_drv_vbus(dev->platform_data, 1); /* Workaround: b_host can't driver * vbus, but PP in PORTSC needs to * be 1 for host to work. @@ -463,7 +522,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on) otg_suspend_state); if (fsm->id) /* default-b */ - fsl_otg_drv_vbus(0); + fsl_otg_drv_vbus(dev->platform_data, 0); } otg_dev->host_working = 0; } @@ -481,7 +540,6 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) struct device *dev; struct platform_driver *gadget_pdrv; struct platform_device *gadget_pdev; - if (!xceiv->gadget || !xceiv->gadget->dev.parent) return -ENODEV; @@ -517,7 +575,6 @@ static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) if (host) { VDBG("host off......\n"); - otg_p->host->otg_port = fsl_otg_initdata.otg_port; otg_p->host->is_b_host = otg_dev->fsm.id; /* must leave time for khubd to finish its thing @@ -634,11 +691,29 @@ static void fsl_otg_event(struct work_struct *work) { struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); struct otg_fsm *fsm = &og->fsm; + struct otg_transceiver *otg = &og->otg; + + otg->default_a = (fsm->id == 0); + /* clear conn information */ + if (fsm->id) + fsm->b_conn = 0; + else + fsm->a_conn = 0; + + if (otg->host) + otg->host->is_b_host = fsm->id; + if (otg->gadget) + otg->gadget->is_a_peripheral = !fsm->id; if (fsm->id) { /* switch to gadget */ + b_session_irq_enable(true); fsl_otg_start_host(fsm, 0); otg_drv_vbus(fsm, 0); fsl_otg_start_gadget(fsm, 1); + }else { /* switch to host */ + fsl_otg_start_gadget(fsm, 0); + otg_drv_vbus(fsm, 1); + fsl_otg_start_host(fsm, 1); } } @@ -678,12 +753,13 @@ irqreturn_t fsl_otg_isr_gpio(int irq, void *dev_id) struct otg_fsm *fsm; struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data *)dev_id; - struct fsl_otg *p_otg; + struct fsl_otg *f_otg; struct otg_transceiver *otg_trans = otg_get_transceiver(); - p_otg = container_of(otg_trans, struct fsl_otg, otg); - fsm = &p_otg->fsm; int value; + f_otg = container_of(otg_trans, struct fsl_otg, otg); + fsm = &f_otg->fsm; + fsl_otg_clk_ctl(); if (pdata->id_gpio == 0) return IRQ_NONE; @@ -695,35 +771,25 @@ irqreturn_t fsl_otg_isr_gpio(int irq, void *dev_id) set_irq_type(gpio_to_irq(pdata->id_gpio), IRQ_TYPE_LEVEL_HIGH); - if (value == p_otg->fsm.id) + if (value == f_otg->fsm.id) return IRQ_HANDLED; - p_otg->fsm.id = value; - - otg_trans->default_a = (fsm->id == 0); - /* clear conn information */ - if (fsm->id) - fsm->b_conn = 0; - else - fsm->a_conn = 0; - - if (otg_trans->host) - otg_trans->host->is_b_host = fsm->id; - if (otg_trans->gadget) - otg_trans->gadget->is_a_peripheral = !fsm->id; - - VDBG("ID int (ID is %d)\n", fsm->id); - if (fsm->id) { /* switch to gadget */ - schedule_delayed_work(&p_otg->otg_event, 100); + f_otg->fsm.id = value; - } else { /* switch to host */ - cancel_delayed_work(&p_otg->otg_event); - fsl_otg_start_gadget(fsm, 0); - otg_drv_vbus(fsm, 1); - fsl_otg_start_host(fsm, 1); + cancel_delayed_work(&f_otg->otg_event); + schedule_delayed_work(&f_otg->otg_event, msecs_to_jiffies(10)); + /* if host mode, we should clear B_SESSION_VLD event and disable + * B_SESSION_VLD irq + */ + if (!f_otg->fsm.id) { + b_session_irq_enable(false); + }else { + //b_session_irq_enable(true); } + return IRQ_HANDLED; } + /* Interrupt handler. OTG/host/peripheral share the same int line. * OTG driver clears OTGSC interrupts and leaves USB interrupts * intact. It needs to have knowledge of some USB interrupts @@ -731,60 +797,70 @@ irqreturn_t fsl_otg_isr_gpio(int irq, void *dev_id) */ irqreturn_t fsl_otg_isr(int irq, void *dev_id) { - struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; - struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg; + struct fsl_otg *fotg = (struct fsl_otg *)dev_id; + struct otg_transceiver *otg = &fotg->otg; u32 otg_int_src, otg_sc; + irqreturn_t ret = IRQ_NONE; + fsl_otg_clk_ctl(); otg_sc = le32_to_cpu(usb_dr_regs->otgsc); otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); - /* Only clear otg interrupts */ - usb_dr_regs->otgsc |= cpu_to_le32(otg_sc & OTGSC_INTSTS_MASK); + /* Only clear otg interrupts, expect B_SESSION_VALID, + * Leave it to be handled by arcotg_udc */ + usb_dr_regs->otgsc = ((usb_dr_regs->otgsc | cpu_to_le32(otg_sc & OTGSC_INTSTS_MASK))& + (~OTGSC_INTSTS_B_SESSION_VALID)); /*FIXME: ID change not generate when init to 0 */ - fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; - otg->default_a = (fsm->id == 0); + fotg->fsm.id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fotg->fsm.id == 0); /* process OTG interrupts */ if (otg_int_src) { if (otg_int_src & OTGSC_INTSTS_USB_ID) { - fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; - otg->default_a = (fsm->id == 0); - /* clear conn information */ - if (fsm->id) - fsm->b_conn = 0; - else - fsm->a_conn = 0; - - if (otg->host) - otg->host->is_b_host = fsm->id; - if (otg->gadget) - otg->gadget->is_a_peripheral = !fsm->id; - VDBG("ID int (ID is %d)\n", fsm->id); - - if (fsm->id) { /* switch to gadget */ - schedule_delayed_work(&((struct fsl_otg *) - dev_id)->otg_event, - 100); - } else { /* switch to host */ - cancel_delayed_work(& - ((struct fsl_otg *)dev_id)-> - otg_event); - fsl_otg_start_gadget(fsm, 0); - otg_drv_vbus(fsm, 1); - fsl_otg_start_host(fsm, 1); + fotg->fsm.id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + + printk("ID int (ID is %d)\n", fotg->fsm.id); + + cancel_delayed_work(&fotg->otg_event); + schedule_delayed_work(&fotg->otg_event, msecs_to_jiffies(10)); + /* if host mode, we should clear B_SESSION_VLD event and disable + * B_SESSION_VLD irq + */ + if (!fotg->fsm.id) { + b_session_irq_enable(false); + }else { + //b_session_irq_enable(true); } - - return IRQ_HANDLED; + ret = IRQ_HANDLED; } } - return IRQ_NONE; + return ret; +} + +static void fsl_otg_fsm_drv_vbus(int on) +{ + struct otg_fsm *fsm = &(fsl_otg_dev->fsm); + struct otg_transceiver *xceiv = fsm->transceiver; + + struct device *dev; + /* + * The host is assigned at otg_set_host + */ + if (!xceiv->host) + return; + /* + * The dev is assigned at usb_create_hcd which is called earlier + * than otg_set_host at host driver's probe + */ + dev = xceiv->host->controller; + fsl_otg_drv_vbus(dev->platform_data, on); } static struct otg_fsm_ops fsl_otg_ops = { .chrg_vbus = fsl_otg_chrg_vbus, - .drv_vbus = fsl_otg_drv_vbus, + .drv_vbus = fsl_otg_fsm_drv_vbus, .loc_conn = fsl_otg_loc_conn, .loc_sof = fsl_otg_loc_sof, .start_pulse = fsl_otg_start_pulse, @@ -837,6 +913,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_tc->otg.set_power = fsl_otg_set_power; fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp; fsl_otg_tc->otg.start_srp = fsl_otg_start_srp; + fsl_otg_tc->otg.dev = &pdev->dev; fsl_otg_dev = fsl_otg_tc; @@ -903,6 +980,8 @@ int usb_otg_start(struct platform_device *pdev) if (pdata->platform_init && pdata->platform_init(pdev) != 0) return -EINVAL; + clk_stopped = false; /* platform_init will open the otg clk */ + /* stop the controller */ temp = readl(&p_otg->dr_mem_map->usbcmd); temp &= ~USB_CMD_RUN_STOP; @@ -1229,6 +1308,10 @@ static int __init fsl_otg_probe(struct platform_device *pdev) return -EIO; } + last_busy = jiffies; + setup_timer(&monitor_timer, fsl_otg_loading_monitor, (unsigned long)pdev); + mod_timer(&monitor_timer, jiffies + msecs_to_jiffies(TIMER_FREQ)); + create_proc_file(); return status; } |