#endif
#include "musbdefs.h"
-// #ifdef CONFIG_USB_MUSB_HDRC_HCD
-#define VBUSERR_RETRY_COUNT 2 /* is this too few? */
-// #endif
#ifdef CONFIG_ARCH_DAVINCI
}
if (bIntrUSB & MGC_M_INTR_VBUSERROR) {
+ int ignore = 0;
+
+ /* During connection as an A-Device, we may see a short
+ * current spikes causing voltage drop, because of cable
+ * and peripheral capacitance combined with vbus draw.
+ * (So: less common with truly self-powered devices, where
+ * vbus doesn't act like a power supply.)
+ *
+ * Such spikes are short; usually less than ~500 usec, max
+ * of ~2 msec. That is, they're not sustained overcurrent
+ * errors, though they're reported using VBUSERROR irqs.
+ *
+ * Workarounds: (a) hardware: use self powered devices.
+ * (b) software: ignore non-repeated VBUS errors.
+ *
+ * REVISIT: do delays from lots of DEBUG_KERNEL checks
+ * make trouble here, keeping VBUS < 4.4V ?
+ */
+ switch (pThis->xceiv.state) {
+ case OTG_STATE_A_HOST:
+ /* recovery is dicey once we've gotten past the
+ * initial stages of enumeration, but if VBUS
+ * stayed ok at the other end of the link, and
+ * another reset is due (at least for high speed,
+ * to redo the chirp etc), it might work OK...
+ */
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_WAIT_VRISE:
+ if (pThis->vbuserr_retry) {
+ pThis->vbuserr_retry--;
+ ignore = 1;
+ devctl |= MGC_M_DEVCTL_SESSION;
+ musb_writeb(pBase, MGC_O_HDRC_DEVCTL, devctl);
+ } else {
+ pThis->port1_status |=
+ (1 << USB_PORT_FEAT_OVER_CURRENT)
+ | (1 << USB_PORT_FEAT_C_OVER_CURRENT);
+ }
+ break;
+ default:
+ break;
+ }
- DBG(1, "VBUS_ERROR (%02x, %s), retry #%d\n", devctl,
+ DBG(1, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
+ otg_state_string(pThis),
+ devctl,
({ char *s;
switch (devctl & MGC_M_DEVCTL_VBUS) {
case 0 << MGC_S_DEVCTL_VBUS:
default:
s = "VALID"; break;
}; s; }),
- pThis->vbuserr_retry);
+ VBUSERR_RETRY_COUNT - pThis->vbuserr_retry,
+ pThis->port1_status);
/* go through A_WAIT_VFALL then start a new session */
- musb_set_vbus(pThis, 0);
+ if (!ignore)
+ musb_set_vbus(pThis, 0);
handled = IRQ_HANDLED;
- } else
- pThis->vbuserr_retry = VBUSERR_RETRY_COUNT;
+ }
if (bIntrUSB & MGC_M_INTR_CONNECT) {
handled = IRQ_HANDLED;
pThis->xceiv.state = OTG_STATE_B_HOST;
break;
default:
- pThis->xceiv.state = OTG_STATE_A_HOST;
+ if ((devctl & MGC_M_DEVCTL_VBUS)
+ == (3 << MGC_S_DEVCTL_VBUS))
+ pThis->xceiv.state = OTG_STATE_A_HOST;
break;
}
- DBG(1, "CONNECT (%s)\n", otg_state_string(pThis));
+ DBG(1, "CONNECT (%s) devctl %02x\n",
+ otg_state_string(pThis), devctl);
}
#endif /* CONFIG_USB_MUSB_HDRC_HCD */
#endif
if ((bIntrUSB & MGC_M_INTR_DISCONNECT) && !pThis->bIgnoreDisconnect) {
- DBG(1, "DISCONNECT as %s, devctl %02x\n",
+ DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
+ otg_state_string(pThis),
MUSB_MODE(pThis), devctl);
handled = IRQ_HANDLED;
- pThis->is_active = 0;
- /* need to check it against pThis, because devctl is going
- * to report ID low as soon as the device gets disconnected
- */
- if (is_host_active(pThis))
+ switch (pThis->xceiv.state) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_HOST:
+ case OTG_STATE_A_SUSPEND:
musb_root_disconnect(pThis);
- else
+ break;
+#endif /* HOST */
+#ifdef CONFIG_USB_MUSB_OTG
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_B_HOST:
+ musb_root_disconnect(pThis);
+ /* FALLTHROUGH */
+ case OTG_STATE_B_WAIT_ACON:
+#endif /* OTG */
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ case OTG_STATE_B_PERIPHERAL:
musb_g_disconnect(pThis);
+ break;
+#endif /* GADGET */
+ default:
+ WARN("unhandled DISCONNECT transition (%s)\n",
+ otg_state_string(pThis));
+ break;
+ }
schedule_work(&pThis->irq_work);
}
if (musb->int_usb & STAGE0_MASK)
retval |= musb_stage0_irq(musb, musb->int_usb,
devctl, power);
- else
- musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
/* "stage 1" is handling endpoint irqs */
int vbus;
spin_lock_irqsave(&musb->Lock, flags);
-#ifdef CONFIG_USB_TUSB6010
+#if defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_USB_MUSB_OTG)
/* REVISIT: connect-A != connect-B ... */
vbus = musb_platform_get_vbus_status(musb);
if (vbus)
u32 conf, prcm, timer;
u8 devctl;
- /* we control CPEN in software not hardware ... */
+ /* HDRC controls CPEN, but beware current surges during device
+ * connect. They can trigger transient overcurrent conditions
+ * that must be ignored.
+ */
prcm = musb_readl(base, TUSB_PRCM_MNGMT);
conf = musb_readl(base, TUSB_DEV_CONF);
if (is_on) {
musb->is_active = 1;
- prcm |= TUSB_PRCM_MNGMT_5V_CPEN;
timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
musb->xceiv.default_a = 1;
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MGC_M_DEVCTL_SESSION;
conf |= TUSB_DEV_CONF_USB_HOST_MODE;
-
+ MUSB_HST_MODE(musb);
} else {
- prcm &= ~TUSB_PRCM_MNGMT_5V_CPEN;
+ musb->is_active = 0;
timer = 0;
- if (musb->xceiv.default_a) {
- musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
- devctl &= ~MGC_M_DEVCTL_SESSION;
- } else {
- musb->xceiv.state = OTG_STATE_B_IDLE;
- musb->is_active = 0;
- }
+ /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
+ * jumping right to B_IDLE...
+ */
+
+ musb->xceiv.default_a = 0;
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ devctl &= ~MGC_M_DEVCTL_SESSION;
+
+ conf &= ~TUSB_DEV_CONF_USB_HOST_MODE;
+ MUSB_DEV_MODE(musb);
}
prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
/* B-dev state machine: no vbus ~= disconnect */
if ((is_otg_enabled(musb) && !musb->xceiv.default_a)
|| !is_host_enabled(musb)) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ // ? musb_root_disconnect(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
+ );
+#endif
if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
if (musb->xceiv.state != OTG_STATE_B_IDLE) {
*/
break;
case OTG_STATE_A_WAIT_VFALL:
- /* REVISIT this irq triggers at too high a
- * voltage ... we probably need to use the
- * OTG timer to wait for session end.
+ /* REVISIT this irq triggers during short
+ * spikes causet by enumeration ...
*/
if (musb->vbuserr_retry) {
musb->vbuserr_retry--;
switch (musb->xceiv.state) {
case OTG_STATE_A_WAIT_VRISE:
- /* VBUS has probably been valid for a while now */
+ /* VBUS has probably been valid for a while now,
+ * but may well have bounced out of range a bit
+ */
devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);
if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) {
+ u32 timer;
+
if ((devctl & MGC_M_DEVCTL_VBUS)
!= MGC_M_DEVCTL_VBUS) {
DBG(2, "devctl %02x\n", devctl);
break;
}
+ musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
- /* request a session, then DEVCTL_HM will
- * be set by the controller
+ /* REVISIT: if nothing is connected yet,
+ * mark controller as inactive so that
+ * we can suspend the TUSB chip.
*/
- devctl |= MGC_M_DEVCTL_SESSION;
- musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL,
- devctl);
- musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
/* timeout 0 == infinite (like non-OTG hosts) */
- if (OTG_TIME_A_WAIT_BCON)
+ timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_BCON);
+ if (timer)
musb_writel(base, TUSB_DEV_OTG_TIMER,
- OTG_TIMER_MS(OTG_TIME_A_WAIT_BCON));
- else
- musb_writel(base, TUSB_DEV_OTG_TIMER, 0);
+ timer);
} else {
+ /* REVISIT report overcurrent to hub? */
ERR("vbus too slow, devctl %02x\n", devctl);
tusb_set_vbus(musb, 0);
}
default:
break;
}
- musb_writel(base, TUSB_DEV_OTG_TIMER, 0);
}
}
musb_writel(base, TUSB_VLYNQ_CTRL, 8);
/* Select PHY free running 60MHz as a system clock */
- musb_writel(base, TUSB_PRCM_CONF, //FIXME: CPEN should not be needed!
- TUSB_PRCM_CONF_SFW_CPEN | TUSB_PRCM_CONF_SYS_CLKSEL(1));
+ tusb_set_clock_source(musb, 1);
/* VBus valid timer 1us, disable DFT/Debug and VLYNQ clocks for
* power saving, enable VBus detect and session end comparators,