Commit 68a37991 authored by David Brownell's avatar David Brownell Committed by Tony Lindgren

musb_hdrc: Update peripheral side suspend support

Update peripheral side suspend support:

 - Work around some IRQ strangeness (disconnect not detected reliably
   while suspended).

 - Swaps out clearly-broken SRP logic (from Mentor?) with code which
   at least matches the documentation.

 - Track whether peripheral is suspended; this helps detect invalid remote
   wakeup requests, and will also help work around a tusb6010 peripheral
   side issue (separate patch).

 - For tusb6010, don't be so noisy with tps65030 idle/suspend messaging;
   among other things, it interferes with debugging.

 - In OTG mode, host side can now autosuspend when peripheral side is
   active (connected or otherwise).

USBCV remote wakeup tests now pass.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
parent 52405e0d
...@@ -330,6 +330,10 @@ __acquires(musb->Lock) ...@@ -330,6 +330,10 @@ __acquires(musb->Lock)
musb->g.b_hnp_enable = 1; musb->g.b_hnp_enable = 1;
devctl = musb_readb(pBase, devctl = musb_readb(pBase,
MGC_O_HDRC_DEVCTL); MGC_O_HDRC_DEVCTL);
/* NOTE: at least DaVinci doesn't
* like to set HR ...
*/
DBG(1, "set HR\n");
musb_writeb(pBase, MGC_O_HDRC_DEVCTL, musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
devctl | MGC_M_DEVCTL_HR); devctl | MGC_M_DEVCTL_HR);
} }
......
...@@ -1426,54 +1426,47 @@ static int musb_gadget_get_frame(struct usb_gadget *gadget) ...@@ -1426,54 +1426,47 @@ static int musb_gadget_get_frame(struct usb_gadget *gadget)
static int musb_gadget_wakeup(struct usb_gadget *gadget) static int musb_gadget_wakeup(struct usb_gadget *gadget)
{ {
struct musb *musb = gadget_to_musb(gadget); struct musb *musb = gadget_to_musb(gadget);
void __iomem *mregs = musb->pRegs;
unsigned long flags; unsigned long flags;
int status = -EINVAL; int status = -EINVAL;
u8 power; u8 power, devctl;
spin_lock_irqsave(&musb->Lock, flags); spin_lock_irqsave(&musb->Lock, flags);
/* fail if we're not suspended */
power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER);
if (!(power & MGC_M_POWER_SUSPENDM))
goto done;
switch (musb->xceiv.state) { switch (musb->xceiv.state) {
case OTG_STATE_B_PERIPHERAL: case OTG_STATE_B_PERIPHERAL:
/* NOTE: OTG state machine doesn't include B_SUSPENDED; /* NOTE: OTG state machine doesn't include B_SUSPENDED;
* that's part of the standard usb 1.1 state machine, and * that's part of the standard usb 1.1 state machine, and
* doesn't affect OTG transitions. * doesn't affect OTG transitions.
*/ */
if (musb->may_wakeup) if (musb->may_wakeup && musb->is_suspended)
break; break;
goto done; goto done;
case OTG_STATE_B_IDLE: case OTG_STATE_B_IDLE:
/* REVISIT we might be able to do SRP even without OTG, /* Start SRP ... OTG not required. */
* though Linux doesn't yet expose that capability. SRP devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
* starts by setting DEVCTL.SESSION (not POWER.RESUME); devctl |= MGC_M_DEVCTL_SESSION;
* though DaVinci can't do it. musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl);
*/ DBG(2, "SRP\n");
if (is_otg_enabled(musb)) { status = 0;
musb->xceiv.state = OTG_STATE_B_SRP_INIT; goto done;
break;
}
/* FALLTHROUGH */
default: default:
goto done; goto done;
} }
status = 0; status = 0;
power = musb_readb(mregs, MGC_O_HDRC_POWER);
power |= MGC_M_POWER_RESUME; power |= MGC_M_POWER_RESUME;
musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power); musb_writeb(mregs, MGC_O_HDRC_POWER, power);
DBG(2, "issue wakeup\n");
/* FIXME do this next chunk in a timer callback, no udelay */ /* FIXME do this next chunk in a timer callback, no udelay */
mdelay(2); mdelay(2);
power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER); power = musb_readb(mregs, MGC_O_HDRC_POWER);
power &= ~MGC_M_POWER_RESUME; power &= ~MGC_M_POWER_RESUME;
musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power); musb_writeb(mregs, MGC_O_HDRC_POWER, power);
if (musb->xceiv.state == OTG_STATE_B_SRP_INIT)
musb->xceiv.state = OTG_STATE_B_IDLE;
done: done:
spin_unlock_irqrestore(&musb->Lock, flags); spin_unlock_irqrestore(&musb->Lock, flags);
return status; return status;
...@@ -1898,6 +1891,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver); ...@@ -1898,6 +1891,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
void musb_g_resume(struct musb *musb) void musb_g_resume(struct musb *musb)
{ {
musb->is_suspended = 0;
switch (musb->xceiv.state) { switch (musb->xceiv.state) {
case OTG_STATE_B_IDLE: case OTG_STATE_B_IDLE:
break; break;
...@@ -1930,6 +1924,7 @@ void musb_g_suspend(struct musb *musb) ...@@ -1930,6 +1924,7 @@ void musb_g_suspend(struct musb *musb)
musb->xceiv.state = OTG_STATE_B_PERIPHERAL; musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
break; break;
case OTG_STATE_B_PERIPHERAL: case OTG_STATE_B_PERIPHERAL:
musb->is_suspended = 1;
if (musb->pGadgetDriver && musb->pGadgetDriver->suspend) { if (musb->pGadgetDriver && musb->pGadgetDriver->suspend) {
spin_unlock(&musb->Lock); spin_unlock(&musb->Lock);
musb->pGadgetDriver->suspend(&musb->g); musb->pGadgetDriver->suspend(&musb->g);
...@@ -2017,6 +2012,7 @@ __acquires(musb->Lock) ...@@ -2017,6 +2012,7 @@ __acquires(musb->Lock)
/* start in USB_STATE_DEFAULT */ /* start in USB_STATE_DEFAULT */
musb->is_active = 1; musb->is_active = 1;
musb->is_suspended = 0;
MUSB_DEV_MODE(musb); MUSB_DEV_MODE(musb);
musb->bAddress = 0; musb->bAddress = 0;
musb->ep0_state = MGC_END0_STAGE_SETUP; musb->ep0_state = MGC_END0_STAGE_SETUP;
......
...@@ -2141,7 +2141,10 @@ static int musb_bus_suspend(struct usb_hcd *hcd) ...@@ -2141,7 +2141,10 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
{ {
struct musb *musb = hcd_to_musb(hcd); struct musb *musb = hcd_to_musb(hcd);
return musb->is_active ? -EBUSY : 0; if (is_host_active(musb) && musb->is_active)
return -EBUSY;
else
return 0;
} }
static int musb_bus_resume(struct usb_hcd *hcd) static int musb_bus_resume(struct usb_hcd *hcd)
......
...@@ -534,6 +534,15 @@ static int dump_header_stats(struct musb *pThis, char *buffer) ...@@ -534,6 +534,15 @@ static int dump_header_stats(struct musb *pThis, char *buffer)
count += code; count += code;
buffer += code; buffer += code;
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
code = sprintf(buffer, "Peripheral address: %02x\n",
musb_readb(pThis, MGC_O_HDRC_FADDR));
if (code <= 0)
goto done;
buffer += code;
count += code;
#endif
#ifdef CONFIG_USB_MUSB_HDRC_HCD #ifdef CONFIG_USB_MUSB_HDRC_HCD
code = sprintf(buffer, "Root port status: %08x\n", code = sprintf(buffer, "Root port status: %08x\n",
pThis->port1_status); pThis->port1_status);
......
...@@ -433,6 +433,9 @@ struct musb { ...@@ -433,6 +433,9 @@ struct musb {
#endif #endif
#ifdef CONFIG_USB_GADGET_MUSB_HDRC #ifdef CONFIG_USB_GADGET_MUSB_HDRC
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
/* may_wakeup means remote wakeup is enabled */ /* may_wakeup means remote wakeup is enabled */
unsigned may_wakeup:1; unsigned may_wakeup:1;
......
...@@ -368,6 +368,15 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, ...@@ -368,6 +368,15 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
#ifdef CONFIG_USB_GADGET_MUSB_HDRC #ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_WAIT_ACON:
case OTG_STATE_B_PERIPHERAL: case OTG_STATE_B_PERIPHERAL:
/* disconnect while suspended? we may
* not get a disconnect irq...
*/
if ((devctl & MGC_M_DEVCTL_VBUS)
!= (3 << MGC_S_DEVCTL_VBUS)) {
pThis->int_usb |= MGC_M_INTR_DISCONNECT;
pThis->int_usb &= ~MGC_M_INTR_SUSPEND;
break;
}
musb_g_resume(pThis); musb_g_resume(pThis);
break; break;
case OTG_STATE_B_IDLE: case OTG_STATE_B_IDLE:
...@@ -646,8 +655,8 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, ...@@ -646,8 +655,8 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
} }
if (bIntrUSB & MGC_M_INTR_SUSPEND) { if (bIntrUSB & MGC_M_INTR_SUSPEND) {
DBG(1, "SUSPEND (%s) devctl %02x\n", DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
otg_state_string(pThis), devctl); otg_state_string(pThis), devctl, power);
handled = IRQ_HANDLED; handled = IRQ_HANDLED;
switch (pThis->xceiv.state) { switch (pThis->xceiv.state) {
......
...@@ -199,7 +199,7 @@ static void tusb_set_clock_source(struct musb *musb, unsigned mode) ...@@ -199,7 +199,7 @@ static void tusb_set_clock_source(struct musb *musb, unsigned mode)
/* 0 = refclk (clkin, XI) /* 0 = refclk (clkin, XI)
* 1 = PHY 60 MHz (internal PLL) * 1 = PHY 60 MHz (internal PLL)
* 2 = not supported * 2 = not supported
* 3 = NOR clock (huh?) * 3 = what?
*/ */
if (mode > 0) if (mode > 0)
reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3); reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3);
...@@ -242,7 +242,7 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) ...@@ -242,7 +242,7 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE; reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE;
musb_writel(base, TUSB_PRCM_MNGMT, reg); musb_writel(base, TUSB_PRCM_MNGMT, reg);
DBG(2, "idle, wake on %02x\n", wakeup_enables); DBG(6, "idle, wake on %02x\n", wakeup_enables);
} }
/* /*
...@@ -545,7 +545,7 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci) ...@@ -545,7 +545,7 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci)
reg = musb_readl(base, TUSB_SCRATCH_PAD); reg = musb_readl(base, TUSB_SCRATCH_PAD);
if (reg == i) if (reg == i)
break; break;
DBG(1, "TUSB NOR not ready\n"); DBG(6, "TUSB NOR not ready\n");
} }
/* work around issue 13 (2nd half) */ /* work around issue 13 (2nd half) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment