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)
pThis->g.b_hnp_enable = 1;
devctl = musb_readb(pBase,
MGC_O_HDRC_DEVCTL);
/* REVISIT after roleswitch, HR will
* have been cleared ... reset it
*/
musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
devctl | MGC_M_DEVCTL_HR);
}
......
......@@ -1449,6 +1449,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
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->bMayWakeup)
break;
goto done;
......@@ -1470,12 +1474,14 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power);
/* 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 &= ~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;
done:
spin_unlock_irqrestore(&musb->Lock, flags);
return status;
......@@ -1929,7 +1935,8 @@ void musb_g_suspend(struct musb *pThis)
/* REVISIT if B_HOST, clear DEVCTL.HOSTREQ;
* 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)
struct dma_channel *dma;
dma = is_in ? ep->rx_channel : ep->tx_channel;
status = ep->musb->pDmaController->channel_abort(dma);
DBG(status ? 1 : 3, "abort %cX%d DMA for urb %p --> %d\n",
is_in ? 'R' : 'T', ep->bLocalEnd, urb, status);
urb->actual_length += dma->dwActualLength;
if (dma) {
status = ep->musb->pDmaController->channel_abort(dma);
DBG(status ? 1 : 3,
"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 ... */
......@@ -2130,6 +2134,19 @@ static void musb_h_stop(struct usb_hcd *hcd)
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 = {
.description = "musb-hcd",
.product_desc = "MUSB HDRC host driver",
......@@ -2151,8 +2168,8 @@ const struct hc_driver musb_hc_driver = {
.hub_status_data = musb_hub_status_data,
.hub_control = musb_hub_control,
// .bus_suspend = musb_bus_suspend,
// .bus_resume = musb_bus_resume,
.bus_suspend = musb_bus_suspend,
.bus_resume = musb_bus_resume,
// .start_port_reset = NULL,
// .hub_irq_enable = NULL,
};
......@@ -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,
u16 typeReq, u16 wValue, u16 wIndex,
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;
......
......@@ -376,6 +376,9 @@ struct musb {
#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;
unsigned long rh_timer;
......
......@@ -131,10 +131,10 @@ MODULE_PARM_DESC(debug, "initial debug message level");
#define MUSB_VERSION_SUFFIX "/dbg"
#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 MUSB_VERSION_BASE "2.2a/db-0.5.1"
#define MUSB_VERSION_BASE "2.2a/db-0.5.2"
#ifndef MUSB_VERSION_SUFFIX
#define MUSB_VERSION_SUFFIX ""
......@@ -149,12 +149,6 @@ MODULE_DESCRIPTION(DRIVER_INFO);
MODULE_AUTHOR(DRIVER_AUTHOR);
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,
if (devctl & MGC_M_DEVCTL_HM) {
#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;
musb_writeb(pBase, MGC_O_HDRC_POWER,
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 */
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
} else {
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
......@@ -506,10 +511,8 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
}
#endif /* CONFIG_USB_MUSB_HDRC_HCD */
/* saved one bit: bus reset and babble share the same bit;
* If I am host is a babble! i must be the only one allowed
* to reset the bus; when in otg mode it means that I have
* to switch to device
/* mentor saves a bit: bus reset and babble share the same irq.
* only host sees babble; only peripheral sees bus reset.
*/
if (bIntrUSB & MGC_M_INTR_RESET) {
if (devctl & MGC_M_DEVCTL_HM) {
......@@ -1006,6 +1009,10 @@ static int __devinit ep_config_from_table(struct musb *musb)
offset = fifo_setup(musb, hw_ep, &ep0_cfg, 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++) {
u8 epn = cfg->hw_ep_num;
......
......@@ -44,7 +44,6 @@
#include "musbdefs.h"
static void musb_port_suspend(struct musb *musb, u8 bSuspend)
{
u8 power;
......@@ -53,26 +52,46 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend)
if (!is_host_active(musb))
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);
if (bSuspend) {
DBG(3, "Root port suspended\n");
musb_writeb(pBase, MGC_O_HDRC_POWER,
power | MGC_M_POWER_SUSPENDM);
power &= ~MGC_M_POWER_RESUME;
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->is_active = is_otg_enabled(musb)
&& musb->xceiv.host->b_hnp_enable;
musb_platform_try_idle(musb);
switch (musb->xceiv.state) {
case OTG_STATE_A_HOST:
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) {
DBG(3, "Root port resumed\n");
musb_writeb(pBase, MGC_O_HDRC_POWER,
power | MGC_M_POWER_RESUME);
musb->is_active = 1;
power &= ~MGC_M_POWER_SUSPENDM;
power |= MGC_M_POWER_RESUME;
musb_writeb(pBase, MGC_O_HDRC_POWER, power);
musb->port1_status &= ~USB_PORT_STAT_SUSPEND;
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
usb_hcd_poll_rh_status(musb_to_hcd(musb));
DBG(3, "Root port resuming, power %02x\n", power);
/* 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)
void musb_root_disconnect(struct musb *musb)
{
musb->port1_status &=
~(USB_PORT_STAT_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;
musb->port1_status = (1 << USB_PORT_FEAT_POWER)
| (1 << USB_PORT_FEAT_C_CONNECTION);
usb_hcd_poll_rh_status(musb_to_hcd(musb));
musb->is_active = 0;
......@@ -255,11 +269,39 @@ int musb_hub_control(
if (wIndex != 1)
goto error;
/* finish RESET signaling? */
if ((musb->port1_status & USB_PORT_STAT_RESET)
&& time_after(jiffies, musb->rh_timer))
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 */
DBG((*(u16*)(buf+2)) ? 2 : 5, "port status %08x\n",
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