Commit 9ea5fd96 authored by David Brownell's avatar David Brownell Committed by Tony Lindgren

musb_hdrc: host side suspend/resume mostly behaves

Host side suspend/resume updates:

 - Add the missing "bus-wide" suspend/resume calls, which in this case
   leave everything up to the root port suspend code.

 - Scrub out state more completely on disconnect

 - Remote wakeup IRQ handling (untested because of enumeration issues)

Also:

 - Handle a host side oops if DMA is disabled as a module parameter

 - Minor cleanups of unused symbols

 - Update version string

And some very minor, related, peripheral side fixes.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
parent efccec38
...@@ -330,9 +330,6 @@ __acquires(pThis->Lock) ...@@ -330,9 +330,6 @@ __acquires(pThis->Lock)
pThis->g.b_hnp_enable = 1; pThis->g.b_hnp_enable = 1;
devctl = musb_readb(pBase, devctl = musb_readb(pBase,
MGC_O_HDRC_DEVCTL); MGC_O_HDRC_DEVCTL);
/* REVISIT after roleswitch, HR will
* have been cleared ... reset it
*/
musb_writeb(pBase, MGC_O_HDRC_DEVCTL, musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
devctl | MGC_M_DEVCTL_HR); devctl | MGC_M_DEVCTL_HR);
} }
......
...@@ -1449,6 +1449,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) ...@@ -1449,6 +1449,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
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;
* that's part of the standard usb 1.1 state machine, and
* doesn't affect OTG transitions.
*/
if (musb->bMayWakeup) if (musb->bMayWakeup)
break; break;
goto done; goto done;
...@@ -1470,12 +1474,14 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) ...@@ -1470,12 +1474,14 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power); musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power);
/* FIXME do this next chunk in a timer callback, no udelay */ /* FIXME do this next chunk in a timer callback, no udelay */
mdelay(10); mdelay(2);
power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER); power = musb_readb(musb->pRegs, 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(musb->pRegs, 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;
...@@ -1929,7 +1935,8 @@ void musb_g_suspend(struct musb *pThis) ...@@ -1929,7 +1935,8 @@ void musb_g_suspend(struct musb *pThis)
/* REVISIT if B_HOST, clear DEVCTL.HOSTREQ; /* REVISIT if B_HOST, clear DEVCTL.HOSTREQ;
* A_PERIPHERAL may need care too * A_PERIPHERAL may need care too
*/ */
WARN("unhandled SUSPEND transition (%d)\n", pThis->xceiv.state); WARN("unhandled SUSPEND transition (%s)\n",
otg_state_string(pThis));
} }
} }
......
...@@ -1929,10 +1929,14 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in) ...@@ -1929,10 +1929,14 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in)
struct dma_channel *dma; struct dma_channel *dma;
dma = is_in ? ep->rx_channel : ep->tx_channel; dma = is_in ? ep->rx_channel : ep->tx_channel;
status = ep->musb->pDmaController->channel_abort(dma); if (dma) {
DBG(status ? 1 : 3, "abort %cX%d DMA for urb %p --> %d\n", status = ep->musb->pDmaController->channel_abort(dma);
is_in ? 'R' : 'T', ep->bLocalEnd, urb, status); DBG(status ? 1 : 3,
urb->actual_length += dma->dwActualLength; "abort %cX%d DMA for urb %p --> %d\n",
is_in ? 'R' : 'T', ep->bLocalEnd,
urb, status);
urb->actual_length += dma->dwActualLength;
}
} }
/* turn off DMA requests, discard state, stop polling ... */ /* turn off DMA requests, discard state, stop polling ... */
...@@ -2130,6 +2134,19 @@ static void musb_h_stop(struct usb_hcd *hcd) ...@@ -2130,6 +2134,19 @@ static void musb_h_stop(struct usb_hcd *hcd)
hcd->state = HC_STATE_HALT; hcd->state = HC_STATE_HALT;
} }
static int musb_bus_suspend(struct usb_hcd *hcd)
{
struct musb *musb = hcd_to_musb(hcd);
return musb->is_active ? -EBUSY : 0;
}
static int musb_bus_resume(struct usb_hcd *hcd)
{
/* resuming child port does the work */
return 0;
}
const struct hc_driver musb_hc_driver = { const struct hc_driver musb_hc_driver = {
.description = "musb-hcd", .description = "musb-hcd",
.product_desc = "MUSB HDRC host driver", .product_desc = "MUSB HDRC host driver",
...@@ -2151,8 +2168,8 @@ const struct hc_driver musb_hc_driver = { ...@@ -2151,8 +2168,8 @@ const struct hc_driver musb_hc_driver = {
.hub_status_data = musb_hub_status_data, .hub_status_data = musb_hub_status_data,
.hub_control = musb_hub_control, .hub_control = musb_hub_control,
// .bus_suspend = musb_bus_suspend, .bus_suspend = musb_bus_suspend,
// .bus_resume = musb_bus_resume, .bus_resume = musb_bus_resume,
// .start_port_reset = NULL, // .start_port_reset = NULL,
// .hub_irq_enable = NULL, // .hub_irq_enable = NULL,
}; };
...@@ -89,8 +89,6 @@ extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf); ...@@ -89,8 +89,6 @@ extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf);
extern int musb_hub_control(struct usb_hcd *hcd, extern int musb_hub_control(struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex, u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength); char *buf, u16 wLength);
extern int musb_bus_suspend(struct usb_hcd *);
extern int musb_bus_resume(struct usb_hcd *);
extern const struct hc_driver musb_hc_driver; extern const struct hc_driver musb_hc_driver;
......
...@@ -376,6 +376,9 @@ struct musb { ...@@ -376,6 +376,9 @@ struct musb {
#ifdef CONFIG_USB_MUSB_HDRC_HCD #ifdef CONFIG_USB_MUSB_HDRC_HCD
/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
#define MUSB_PORT_STAT_RESUME (1 << 31)
u32 port1_status; u32 port1_status;
unsigned long rh_timer; unsigned long rh_timer;
......
...@@ -131,10 +131,10 @@ MODULE_PARM_DESC(debug, "initial debug message level"); ...@@ -131,10 +131,10 @@ MODULE_PARM_DESC(debug, "initial debug message level");
#define MUSB_VERSION_SUFFIX "/dbg" #define MUSB_VERSION_SUFFIX "/dbg"
#endif #endif
#define DRIVER_AUTHOR "Mentor Graphics Corp. and Texas Instruments" #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia"
#define DRIVER_DESC "Inventra Dual-Role USB Controller Driver" #define DRIVER_DESC "Inventra Dual-Role USB Controller Driver"
#define MUSB_VERSION_BASE "2.2a/db-0.5.1" #define MUSB_VERSION_BASE "2.2a/db-0.5.2"
#ifndef MUSB_VERSION_SUFFIX #ifndef MUSB_VERSION_SUFFIX
#define MUSB_VERSION_SUFFIX "" #define MUSB_VERSION_SUFFIX ""
...@@ -149,12 +149,6 @@ MODULE_DESCRIPTION(DRIVER_INFO); ...@@ -149,12 +149,6 @@ MODULE_DESCRIPTION(DRIVER_INFO);
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* time (millseconds) to wait before a restart */
#define MUSB_RESTART_TIME 5000
/* how many babbles to allow before giving up */
#define MUSB_MAX_BABBLE_COUNT 10
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -382,17 +376,28 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, ...@@ -382,17 +376,28 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
if (devctl & MGC_M_DEVCTL_HM) { if (devctl & MGC_M_DEVCTL_HM) {
#ifdef CONFIG_USB_MUSB_HDRC_HCD #ifdef CONFIG_USB_MUSB_HDRC_HCD
/* REVISIT: this is where SRP kicks in, yes?
* host responsibility should be to CLEAR the
* resume signaling after 50 msec ...
*/
MUSB_HST_MODE(pThis); /* unnecessary */
power &= ~MGC_M_POWER_SUSPENDM; power &= ~MGC_M_POWER_SUSPENDM;
musb_writeb(pBase, MGC_O_HDRC_POWER, musb_writeb(pBase, MGC_O_HDRC_POWER,
power | MGC_M_POWER_RESUME); power | MGC_M_POWER_RESUME);
/* later, GetPortStatus will stop RESUME signaling */
pThis->port1_status |= MUSB_PORT_STAT_RESUME;
pThis->rh_timer = jiffies + msecs_to_jiffies(20);
/* should now be A_SUSPEND */ /* should now be A_SUSPEND */
pThis->xceiv.state = OTG_STATE_A_HOST; switch (pThis->xceiv.state) {
case OTG_STATE_A_SUSPEND:
pThis->xceiv.state = OTG_STATE_A_HOST;
usb_hcd_resume_root_hub(musb_to_hcd(pThis));
break;
case OTG_STATE_B_WAIT_ACON:
pThis->xceiv.state = OTG_STATE_B_PERIPHERAL;
MUSB_DEV_MODE(pThis);
break;
default:
WARN("bogus RESUME, from %s\n",
otg_state_string(pThis));
}
#endif #endif
} else { } else {
#ifdef CONFIG_USB_GADGET_MUSB_HDRC #ifdef CONFIG_USB_GADGET_MUSB_HDRC
...@@ -506,10 +511,8 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, ...@@ -506,10 +511,8 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
} }
#endif /* CONFIG_USB_MUSB_HDRC_HCD */ #endif /* CONFIG_USB_MUSB_HDRC_HCD */
/* saved one bit: bus reset and babble share the same bit; /* mentor saves a bit: bus reset and babble share the same irq.
* If I am host is a babble! i must be the only one allowed * only host sees babble; only peripheral sees bus reset.
* to reset the bus; when in otg mode it means that I have
* to switch to device
*/ */
if (bIntrUSB & MGC_M_INTR_RESET) { if (bIntrUSB & MGC_M_INTR_RESET) {
if (devctl & MGC_M_DEVCTL_HM) { if (devctl & MGC_M_DEVCTL_HM) {
...@@ -1006,6 +1009,10 @@ static int __devinit ep_config_from_table(struct musb *musb) ...@@ -1006,6 +1009,10 @@ static int __devinit ep_config_from_table(struct musb *musb)
offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0); offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0);
// assert(offset > 0) // assert(offset > 0)
/* NOTE: for RTL versions >= 1.400 EPINFO and RAMINFO would
* be better than static MUSB_C_NUM_EPS and DYN_FIFO_SIZE...
*/
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
u8 epn = cfg->hw_ep_num; u8 epn = cfg->hw_ep_num;
......
...@@ -44,7 +44,6 @@ ...@@ -44,7 +44,6 @@
#include "musbdefs.h" #include "musbdefs.h"
static void musb_port_suspend(struct musb *musb, u8 bSuspend) static void musb_port_suspend(struct musb *musb, u8 bSuspend)
{ {
u8 power; u8 power;
...@@ -53,26 +52,46 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend) ...@@ -53,26 +52,46 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend)
if (!is_host_active(musb)) if (!is_host_active(musb))
return; return;
/* NOTE: this doesn't necessarily put PHY into low power mode,
* turning off its clock; that's a function of PHY integration and
* MGC_M_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect
* SE0 changing to connect (J) or wakeup (K) states.
*/
power = musb_readb(pBase, MGC_O_HDRC_POWER); power = musb_readb(pBase, MGC_O_HDRC_POWER);
if (bSuspend) { if (bSuspend) {
DBG(3, "Root port suspended\n"); power &= ~MGC_M_POWER_RESUME;
musb_writeb(pBase, MGC_O_HDRC_POWER, power |= MGC_M_POWER_SUSPENDM;
power | MGC_M_POWER_SUSPENDM); musb_writeb(pBase, MGC_O_HDRC_POWER, power);
DBG(3, "Root port suspended, power %02x\n", power);
musb->port1_status |= USB_PORT_STAT_SUSPEND; musb->port1_status |= USB_PORT_STAT_SUSPEND;
musb->is_active = is_otg_enabled(musb) switch (musb->xceiv.state) {
&& musb->xceiv.host->b_hnp_enable; case OTG_STATE_A_HOST:
musb_platform_try_idle(musb); musb->xceiv.state = OTG_STATE_A_SUSPEND;
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv.host->b_hnp_enable;
musb_platform_try_idle(musb);
break;
case OTG_STATE_B_HOST:
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
MUSB_DEV_MODE(musb);
/* REVISIT restore setting of MGC_M_DEVCTL_HR */
break;
default:
DBG(1, "bogus rh suspend? %s\n",
otg_state_string(musb));
}
} else if (power & MGC_M_POWER_SUSPENDM) { } else if (power & MGC_M_POWER_SUSPENDM) {
DBG(3, "Root port resumed\n"); power &= ~MGC_M_POWER_SUSPENDM;
musb_writeb(pBase, MGC_O_HDRC_POWER, power |= MGC_M_POWER_RESUME;
power | MGC_M_POWER_RESUME);
musb->is_active = 1;
musb_writeb(pBase, MGC_O_HDRC_POWER, power); musb_writeb(pBase, MGC_O_HDRC_POWER, power);
musb->port1_status &= ~USB_PORT_STAT_SUSPEND;
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; DBG(3, "Root port resuming, power %02x\n", power);
usb_hcd_poll_rh_status(musb_to_hcd(musb));
/* later, GetPortStatus will stop RESUME signaling */
musb->port1_status |= MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies + msecs_to_jiffies(20);
} }
} }
...@@ -131,14 +150,9 @@ static void musb_port_reset(struct musb *musb, u8 bReset) ...@@ -131,14 +150,9 @@ static void musb_port_reset(struct musb *musb, u8 bReset)
void musb_root_disconnect(struct musb *musb) void musb_root_disconnect(struct musb *musb)
{ {
musb->port1_status &= musb->port1_status = (1 << USB_PORT_FEAT_POWER)
~(USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION);
| USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED
| USB_PORT_STAT_TEST
);
musb->port1_status |= USB_PORT_STAT_C_CONNECTION << 16;
usb_hcd_poll_rh_status(musb_to_hcd(musb)); usb_hcd_poll_rh_status(musb_to_hcd(musb));
musb->is_active = 0; musb->is_active = 0;
...@@ -255,11 +269,39 @@ int musb_hub_control( ...@@ -255,11 +269,39 @@ int musb_hub_control(
if (wIndex != 1) if (wIndex != 1)
goto error; goto error;
/* finish RESET signaling? */
if ((musb->port1_status & USB_PORT_STAT_RESET) if ((musb->port1_status & USB_PORT_STAT_RESET)
&& time_after(jiffies, musb->rh_timer)) && time_after(jiffies, musb->rh_timer))
musb_port_reset(musb, FALSE); musb_port_reset(musb, FALSE);
*(__le32 *) buf = cpu_to_le32 (musb->port1_status); /* finish RESUME signaling? */
if ((musb->port1_status & MUSB_PORT_STAT_RESUME)
&& time_after(jiffies, musb->rh_timer)) {
u8 power;
power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER);
power &= ~MGC_M_POWER_RESUME;
DBG(4, "root port resume stopped, power %02x\n",
power);
musb_writeb(musb->pRegs, MGC_O_HDRC_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;
}
*(__le32 *) buf = cpu_to_le32(musb->port1_status
& ~MUSB_PORT_STAT_RESUME);
/* port change status is more interesting */ /* port change status is more interesting */
DBG((*(u16*)(buf+2)) ? 2 : 5, "port status %08x\n", DBG((*(u16*)(buf+2)) ? 2 : 5, "port status %08x\n",
musb->port1_status); musb->port1_status);
......
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