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);
}
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;
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;
/* 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));
}
}
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 ... */
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",
.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,
};
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;
#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;
#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 ""
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
-
/*-------------------------------------------------------------------------*/
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
}
#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) {
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;
#include "musbdefs.h"
-
static void musb_port_suspend(struct musb *musb, u8 bSuspend)
{
u8 power;
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);
}
}
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;
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);