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)
musb->g.b_hnp_enable = 1;
devctl = musb_readb(pBase,
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,
devctl | MGC_M_DEVCTL_HR);
}
......
......@@ -1426,54 +1426,47 @@ static int musb_gadget_get_frame(struct usb_gadget *gadget)
static int musb_gadget_wakeup(struct usb_gadget *gadget)
{
struct musb *musb = gadget_to_musb(gadget);
void __iomem *mregs = musb->pRegs;
unsigned long flags;
int status = -EINVAL;
u8 power;
u8 power, devctl;
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) {
case OTG_STATE_B_PERIPHERAL:
/* NOTE: OTG state machine doesn't include B_SUSPENDED;
* that's part of the standard usb 1.1 state machine, and
* doesn't affect OTG transitions.
*/
if (musb->may_wakeup)
if (musb->may_wakeup && musb->is_suspended)
break;
goto done;
case OTG_STATE_B_IDLE:
/* REVISIT we might be able to do SRP even without OTG,
* though Linux doesn't yet expose that capability. SRP
* starts by setting DEVCTL.SESSION (not POWER.RESUME);
* though DaVinci can't do it.
*/
if (is_otg_enabled(musb)) {
musb->xceiv.state = OTG_STATE_B_SRP_INIT;
break;
}
/* FALLTHROUGH */
/* Start SRP ... OTG not required. */
devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
devctl |= MGC_M_DEVCTL_SESSION;
musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl);
DBG(2, "SRP\n");
status = 0;
goto done;
default:
goto done;
}
status = 0;
power = musb_readb(mregs, MGC_O_HDRC_POWER);
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 */
mdelay(2);
power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER);
power = musb_readb(mregs, MGC_O_HDRC_POWER);
power &= ~MGC_M_POWER_RESUME;
musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power);
if (musb->xceiv.state == OTG_STATE_B_SRP_INIT)
musb->xceiv.state = OTG_STATE_B_IDLE;
musb_writeb(mregs, MGC_O_HDRC_POWER, power);
done:
spin_unlock_irqrestore(&musb->Lock, flags);
return status;
......@@ -1898,6 +1891,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
void musb_g_resume(struct musb *musb)
{
musb->is_suspended = 0;
switch (musb->xceiv.state) {
case OTG_STATE_B_IDLE:
break;
......@@ -1930,6 +1924,7 @@ void musb_g_suspend(struct musb *musb)
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
break;
case OTG_STATE_B_PERIPHERAL:
musb->is_suspended = 1;
if (musb->pGadgetDriver && musb->pGadgetDriver->suspend) {
spin_unlock(&musb->Lock);
musb->pGadgetDriver->suspend(&musb->g);
......@@ -2017,6 +2012,7 @@ __acquires(musb->Lock)
/* start in USB_STATE_DEFAULT */
musb->is_active = 1;
musb->is_suspended = 0;
MUSB_DEV_MODE(musb);
musb->bAddress = 0;
musb->ep0_state = MGC_END0_STAGE_SETUP;
......
......@@ -2141,7 +2141,10 @@ static int musb_bus_suspend(struct usb_hcd *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)
......
......@@ -534,6 +534,15 @@ static int dump_header_stats(struct musb *pThis, char *buffer)
count += 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
code = sprintf(buffer, "Root port status: %08x\n",
pThis->port1_status);
......
......@@ -433,6 +433,9 @@ struct musb {
#endif
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
/* may_wakeup means remote wakeup is enabled */
unsigned may_wakeup:1;
......
......@@ -368,6 +368,15 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
case OTG_STATE_B_WAIT_ACON:
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);
break;
case OTG_STATE_B_IDLE:
......@@ -646,8 +655,8 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
}
if (bIntrUSB & MGC_M_INTR_SUSPEND) {
DBG(1, "SUSPEND (%s) devctl %02x\n",
otg_state_string(pThis), devctl);
DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
otg_state_string(pThis), devctl, power);
handled = IRQ_HANDLED;
switch (pThis->xceiv.state) {
......
......@@ -199,7 +199,7 @@ static void tusb_set_clock_source(struct musb *musb, unsigned mode)
/* 0 = refclk (clkin, XI)
* 1 = PHY 60 MHz (internal PLL)
* 2 = not supported
* 3 = NOR clock (huh?)
* 3 = what?
*/
if (mode > 0)
reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3);
......@@ -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;
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)
reg = musb_readl(base, TUSB_SCRATCH_PAD);
if (reg == i)
break;
DBG(1, "TUSB NOR not ready\n");
DBG(6, "TUSB NOR not ready\n");
}
/* 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