diff options
Diffstat (limited to 'drivers/usb/musb/musb_virthub.c')
| -rw-r--r-- | drivers/usb/musb/musb_virthub.c | 121 |
1 files changed, 73 insertions, 48 deletions
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index f70579154de..e2d2d8c9891 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/time.h> #include <linux/timer.h> @@ -44,8 +43,38 @@ #include "musb_core.h" +void musb_host_finish_resume(struct work_struct *work) +{ + struct musb *musb; + unsigned long flags; + u8 power; + + musb = container_of(work, struct musb, finish_resume_work.work); + + spin_lock_irqsave(&musb->lock, flags); + + power = musb_readb(musb->mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", + power); + musb_writeb(musb->mregs, MUSB_POWER, power); + + /* + * ISSUE: DaVinci (RTL 1.300) disconnects after + * resume of high speed peripherals (but not full + * speed ones). + */ + musb->is_active = 1; + musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME); + musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; + usb_hcd_poll_rh_status(musb->hcd); + /* NOTE: it might really be A_WAIT_BCON ... */ + musb->xceiv->state = OTG_STATE_A_HOST; -static void musb_port_suspend(struct musb *musb, bool do_suspend) + spin_unlock_irqrestore(&musb->lock, flags); +} + +void musb_port_suspend(struct musb *musb, bool do_suspend) { struct usb_otg *otg = musb->xceiv->otg; u8 power; @@ -95,7 +124,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) break; default: dev_dbg(musb->controller, "bogus rh suspend? %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } else if (power & MUSB_POWER_SUSPENDM) { power &= ~MUSB_POWER_SUSPENDM; @@ -106,11 +135,12 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) /* later, GetPortStatus will stop RESUME signaling */ musb->port1_status |= MUSB_PORT_STAT_RESUME; - musb->rh_timer = jiffies + msecs_to_jiffies(20); + schedule_delayed_work(&musb->finish_resume_work, + msecs_to_jiffies(20)); } } -static void musb_port_reset(struct musb *musb, bool do_reset) +void musb_port_reset(struct musb *musb, bool do_reset) { u8 power; void __iomem *mbase = musb->mregs; @@ -129,7 +159,6 @@ static void musb_port_reset(struct musb *musb, bool do_reset) */ power = musb_readb(mbase, MUSB_POWER); if (do_reset) { - /* * If RESUME is set, we must make sure it stays minimum 20 ms. * Then we must clear RESUME and wait a bit to let musb start @@ -138,28 +167,37 @@ static void musb_port_reset(struct musb *musb, bool do_reset) * detected". */ if (power & MUSB_POWER_RESUME) { - while (time_before(jiffies, musb->rh_timer)) - msleep(1); + long remain = (unsigned long) musb->rh_timer - jiffies; + + if (musb->rh_timer > 0 && remain > 0) { + /* take into account the minimum delay after resume */ + schedule_delayed_work( + &musb->deassert_reset_work, remain); + return; + } + musb_writeb(mbase, MUSB_POWER, - power & ~MUSB_POWER_RESUME); - msleep(1); + power & ~MUSB_POWER_RESUME); + + /* Give the core 1 ms to clear MUSB_POWER_RESUME */ + schedule_delayed_work(&musb->deassert_reset_work, + msecs_to_jiffies(1)); + return; } - musb->ignore_disconnect = true; power &= 0xf0; musb_writeb(mbase, MUSB_POWER, power | MUSB_POWER_RESET); musb->port1_status |= USB_PORT_STAT_RESET; musb->port1_status &= ~USB_PORT_STAT_ENABLE; - musb->rh_timer = jiffies + msecs_to_jiffies(50); + schedule_delayed_work(&musb->deassert_reset_work, + msecs_to_jiffies(50)); } else { dev_dbg(musb->controller, "root port reset stopped\n"); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); - musb->ignore_disconnect = false; - power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { dev_dbg(musb->controller, "high-speed device connected\n"); @@ -170,7 +208,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) musb->port1_status |= USB_PORT_STAT_ENABLE | (USB_PORT_STAT_C_RESET << 16) | (USB_PORT_STAT_C_ENABLE << 16); - usb_hcd_poll_rh_status(musb_to_hcd(musb)); + usb_hcd_poll_rh_status(musb->hcd); musb->vbuserr_retry = VBUSERR_RETRY_COUNT; } @@ -183,7 +221,7 @@ void musb_root_disconnect(struct musb *musb) musb->port1_status = USB_PORT_STAT_POWER | (USB_PORT_STAT_C_CONNECTION << 16); - usb_hcd_poll_rh_status(musb_to_hcd(musb)); + usb_hcd_poll_rh_status(musb->hcd); musb->is_active = 0; switch (musb->xceiv->state) { @@ -203,7 +241,7 @@ void musb_root_disconnect(struct musb *musb) break; default: dev_dbg(musb->controller, "host disconnect (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } @@ -224,6 +262,23 @@ int musb_hub_status_data(struct usb_hcd *hcd, char *buf) return retval; } +static int musb_has_gadget(struct musb *musb) +{ + /* + * In host-only mode we start a connection right away. In OTG mode + * we have to wait until we loaded a gadget. We don't really need a + * gadget if we operate as a host but we should not start a session + * as a device without a gadget or else we explode. + */ +#ifdef CONFIG_USB_MUSB_HOST + return 1; +#else + if (musb->port_mode == MUSB_PORT_MODE_HOST) + return 1; + return musb->g.dev.driver != NULL; +#endif +} + int musb_hub_control( struct usb_hcd *hcd, u16 typeReq, @@ -312,36 +367,6 @@ int musb_hub_control( if (wIndex != 1) goto error; - /* finish RESET signaling? */ - if ((musb->port1_status & USB_PORT_STAT_RESET) - && time_after_eq(jiffies, musb->rh_timer)) - musb_port_reset(musb, false); - - /* finish RESUME signaling? */ - if ((musb->port1_status & MUSB_PORT_STAT_RESUME) - && time_after_eq(jiffies, musb->rh_timer)) { - u8 power; - - power = musb_readb(musb->mregs, MUSB_POWER); - power &= ~MUSB_POWER_RESUME; - dev_dbg(musb->controller, "root port resume stopped, power %02x\n", - power); - musb_writeb(musb->mregs, MUSB_POWER, power); - - /* ISSUE: DaVinci (RTL 1.300) disconnects after - * resume of high speed peripherals (but not full - * speed ones). - */ - - musb->is_active = 1; - musb->port1_status &= ~(USB_PORT_STAT_SUSPEND - | MUSB_PORT_STAT_RESUME); - musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; - usb_hcd_poll_rh_status(musb_to_hcd(musb)); - /* NOTE: it might really be A_WAIT_BCON ... */ - musb->xceiv->state = OTG_STATE_A_HOST; - } - put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); @@ -366,7 +391,7 @@ int musb_hub_control( * initialization logic, e.g. for OTG, or change any * logic relating to VBUS power-up. */ - if (!hcd->self.is_b_host) + if (!hcd->self.is_b_host && musb_has_gadget(musb)) musb_start(musb); break; case USB_PORT_FEAT_RESET: |
