From: David Brownell Date: Thu, 9 Nov 2006 20:19:43 +0000 (+0200) Subject: musb_hdrc: host vbus and connect/disconnect fixes X-Git-Tag: v2.6.19-omap1~77 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=41416eec5b8957a7e4b43790ef76fcef565902a4;p=linux-2.6-omap-h63xx.git musb_hdrc: host vbus and connect/disconnect fixes Teach host side VBUS powerup and connect logic about the problem whereby the HDRC core wrongly reports VBUS_ERROR during initial powerup. Remove previous non-working workaround for tusb powering VBUS: don't try handling CPEN by hand. Rest of the fix for disconnection issues in A_SUSPEND. Signed-off-by: David Brownell --- diff --git a/drivers/usb/musb/musbdefs.h b/drivers/usb/musb/musbdefs.h index cea6ea71a57..ce3c77ec338 100644 --- a/drivers/usb/musb/musbdefs.h +++ b/drivers/usb/musb/musbdefs.h @@ -415,6 +415,7 @@ struct musb { struct musb_hw_ep aLocalEnd[MUSB_C_NUM_EPS]; #define control_ep aLocalEnd +#define VBUSERR_RETRY_COUNT 3 u16 vbuserr_retry; u16 wEndMask; u8 bEndCount; diff --git a/drivers/usb/musb/plat_uds.c b/drivers/usb/musb/plat_uds.c index dd471d059d7..b345d6f7fae 100644 --- a/drivers/usb/musb/plat_uds.c +++ b/drivers/usb/musb/plat_uds.c @@ -108,9 +108,6 @@ #endif #include "musbdefs.h" -// #ifdef CONFIG_USB_MUSB_HDRC_HCD -#define VBUSERR_RETRY_COUNT 2 /* is this too few? */ -// #endif #ifdef CONFIG_ARCH_DAVINCI @@ -411,8 +408,52 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, } 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: @@ -425,13 +466,14 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, 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; @@ -468,10 +510,13 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, 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 */ @@ -566,18 +611,35 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, #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); } @@ -1299,8 +1361,6 @@ irqreturn_t musb_interrupt(struct musb *musb) 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 */ @@ -1450,7 +1510,7 @@ musb_cable_show(struct device *dev, struct device_attribute *attr, char *buf) 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) diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 0e3b444edd6..bae3100c66d 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -336,7 +336,10 @@ static void tusb_set_vbus(struct musb *musb, int is_on) 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); @@ -344,25 +347,28 @@ static void tusb_set_vbus(struct musb *musb, int is_on) 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); @@ -402,6 +408,16 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) /* 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) { @@ -426,9 +442,8 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) */ 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--; @@ -449,30 +464,32 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) 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); } @@ -488,7 +505,6 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) default: break; } - musb_writel(base, TUSB_DEV_OTG_TIMER, 0); } } @@ -747,8 +763,7 @@ static int __devinit tusb_start(struct musb *musb) 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, diff --git a/drivers/usb/musb/virthub.c b/drivers/usb/musb/virthub.c index 618251f4c17..41ae741430c 100644 --- a/drivers/usb/musb/virthub.c +++ b/drivers/usb/musb/virthub.c @@ -144,6 +144,7 @@ static void musb_port_reset(struct musb *musb, u8 bReset) | (USB_PORT_STAT_C_ENABLE << 16); usb_hcd_poll_rh_status(musb_to_hcd(musb)); + musb->vbuserr_retry = VBUSERR_RETRY_COUNT; } }