From: Tony Lindgren Date: Fri, 29 Jun 2007 07:09:52 +0000 (-0700) Subject: musb_hdrc: Add support for SRP, HNP and basic host idling X-Git-Tag: v2.6.22-omap1~14 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=7f15a09266167698b3627865456f1f880aa83cba;p=linux-2.6-omap-h63xx.git musb_hdrc: Add support for SRP, HNP and basic host idling Several changes, mostly affecting tusb: - Make SRP and HNP mostly work - Add a timer for handling OTG events - Add a timeout for turning off VBUS in host mode via sysfs Only tested on tusb. Signed-off-by: Tony Lindgren --- diff --git a/drivers/usb/musb/g_ep0.c b/drivers/usb/musb/g_ep0.c index aad53fdd2a3..644aada1bc1 100644 --- a/drivers/usb/musb/g_ep0.c +++ b/drivers/usb/musb/g_ep0.c @@ -200,6 +200,19 @@ static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req) musb_g_giveback(&musb->aLocalEnd[0].ep_in, req, 0); } +/* + * Tries to start B-device HNP negotiation if enabled via sysfs + */ +static inline void musb_try_b_hnp_enable(struct musb *musb) +{ + void __iomem *pBase = musb->pRegs; + u8 devctl; + + DBG(1, "HNP: Setting HR\n"); + devctl = musb_readb(pBase, MGC_O_HDRC_DEVCTL); + musb_writeb(pBase, MGC_O_HDRC_DEVCTL, devctl | MGC_M_DEVCTL_HR); +} + /* * Handle all control requests with no DATA stage, including standard * requests such as: @@ -326,17 +339,8 @@ __acquires(musb->Lock) case USB_DEVICE_B_HNP_ENABLE: if (!musb->g.is_otg) goto stall; - { u8 devctl; musb->g.b_hnp_enable = 1; - devctl = musb_readb(pBase, - MGC_O_HDRC_DEVCTL); - /* NOTE: at least DaVinci doesn't - * like to set HR ... - */ - DBG(1, "set HR\n"); - musb_writeb(pBase, MGC_O_HDRC_DEVCTL, - devctl | MGC_M_DEVCTL_HR); - } + musb_try_b_hnp_enable(musb); break; case USB_DEVICE_A_HNP_SUPPORT: if (!musb->g.is_otg) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index b466495debf..ce389cdbb9f 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1462,10 +1462,15 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) goto done; case OTG_STATE_B_IDLE: /* Start SRP ... OTG not required. */ + DBG(2, "Sending SRP\n"); devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL); devctl |= MGC_M_DEVCTL_SESSION; musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl); - DBG(2, "SRP\n"); + + /* Block idling for at least 1s */ + musb_platform_try_idle(musb, + msecs_to_jiffies(1 * HZ)); + status = 0; goto done; default: @@ -1689,7 +1694,7 @@ int __init musb_gadget_setup(struct musb *musb) musb_g_init_endpoints(musb); musb->is_active = 0; - musb_platform_try_idle(musb); + musb_platform_try_idle(musb, 0); status = device_register(&musb->g.dev); if (status != 0) @@ -1872,6 +1877,11 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) */ spin_lock_irqsave(&musb->Lock, flags); + +#ifdef CONFIG_USB_MUSB_OTG + musb_hnp_stop(musb); +#endif + if (musb->pGadgetDriver == driver) { musb->xceiv.state = OTG_STATE_UNDEFINED; stop_activity(musb, driver); @@ -1885,7 +1895,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) musb->g.dev.driver = NULL; musb->is_active = 0; - musb_platform_try_idle(musb); + musb_platform_try_idle(musb, 0); } else retval = -EINVAL; spin_unlock_irqrestore(&musb->Lock, flags); @@ -1958,6 +1968,12 @@ void musb_g_suspend(struct musb *musb) } } +/* Called during SRP. Caller must hold lock */ +void musb_g_wakeup(struct musb *musb) +{ + musb_gadget_wakeup(&musb->g); +} + /* called when VBUS drops below session threshold, and in other cases */ void musb_g_disconnect(struct musb *musb) { @@ -1984,6 +2000,9 @@ void musb_g_disconnect(struct musb *musb) #ifdef CONFIG_USB_MUSB_OTG musb->xceiv.state = OTG_STATE_A_IDLE; break; + case OTG_STATE_A_PERIPHERAL: + musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; + break; case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_HOST: #endif diff --git a/drivers/usb/musb/musb_procfs.c b/drivers/usb/musb/musb_procfs.c index 5bddf30a3fd..857881e6ab9 100644 --- a/drivers/usb/musb/musb_procfs.c +++ b/drivers/usb/musb/musb_procfs.c @@ -773,7 +773,7 @@ static int musb_proc_write(struct file *file, const char __user *buffer, break; } - musb_platform_try_idle(musb); + musb_platform_try_idle(musb, 0); return count; } @@ -811,7 +811,7 @@ static int musb_proc_read(char *page, char **start, } } - musb_platform_try_idle(pThis); + musb_platform_try_idle(pThis, 0); spin_unlock_irqrestore(&pThis->Lock, flags); *eof = 1; diff --git a/drivers/usb/musb/musbdefs.h b/drivers/usb/musb/musbdefs.h index b534d796035..876798228ba 100644 --- a/drivers/usb/musb/musbdefs.h +++ b/drivers/usb/musb/musbdefs.h @@ -124,6 +124,7 @@ extern void musb_g_rx(struct musb *, u8); extern void musb_g_reset(struct musb *); extern void musb_g_suspend(struct musb *); extern void musb_g_resume(struct musb *); +extern void musb_g_wakeup(struct musb *); extern void musb_g_disconnect(struct musb *); #else @@ -134,6 +135,7 @@ static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; } static inline void musb_g_reset(struct musb *m) {} static inline void musb_g_suspend(struct musb *m) {} static inline void musb_g_resume(struct musb *m) {} +static inline void musb_g_wakeup(struct musb *m) {} static inline void musb_g_disconnect(struct musb *m) {} #endif @@ -417,6 +419,9 @@ struct musb { unsigned bIsHost:1; unsigned bIgnoreDisconnect:1; /* during bus resets */ + int a_wait_bcon; /* VBUS timeout in msecs */ + unsigned long idle_timeout; /* Next timeout in jiffies */ + #ifdef C_MP_TX unsigned bBulkSplit:1; #define can_bulk_split(musb,type) \ @@ -506,12 +511,14 @@ extern irqreturn_t musb_interrupt(struct musb *); extern void musb_platform_enable(struct musb *musb); extern void musb_platform_disable(struct musb *musb); +extern void musb_hnp_stop(struct musb *musb); + #ifdef CONFIG_USB_TUSB6010 -extern void musb_platform_try_idle(struct musb *musb); +extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout); extern int musb_platform_get_vbus_status(struct musb *musb); extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode); #else -#define musb_platform_try_idle(x) do {} while (0) +#define musb_platform_try_idle(x, y) do {} while (0) #define musb_platform_get_vbus_status(x) 0 #define musb_platform_set_mode(x, y) do {} while (0) #endif diff --git a/drivers/usb/musb/plat_uds.c b/drivers/usb/musb/plat_uds.c index 1b7d9669711..3aca510af90 100644 --- a/drivers/usb/musb/plat_uds.c +++ b/drivers/usb/musb/plat_uds.c @@ -281,6 +281,69 @@ void musb_load_testpacket(struct musb *musb) /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_MUSB_OTG + +/* + * See also USB_OTG_1-3.pdf 6.6.5 Timers + * REVISIT: Are the other timers done in the hardware? + */ +#define TB_ASE0_BRST 100 /* Min 3.125 ms */ + +/* + * Handles OTG hnp timeouts, such as b_ase0_brst + */ +void musb_otg_timer_func(unsigned long data) +{ + struct musb *musb = (struct musb *)data; + unsigned long flags; + + spin_lock_irqsave(&musb->Lock, flags); + if (musb->xceiv.state == OTG_STATE_B_WAIT_ACON) { + DBG(1, "HNP: B_WAIT_ACON timeout, going back to B_PERIPHERAL\n"); + musb_g_disconnect(musb); + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->is_active = 0; + } + spin_unlock_irqrestore(&musb->Lock, flags); +} + +static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0); + +/* + * Stops the B-device HNP state. Caller must take care of locking. + */ +void musb_hnp_stop(struct musb *musb) +{ + struct usb_hcd *hcd = musb_to_hcd(musb); + void __iomem *pBase = musb->pRegs; + u8 reg; + + switch (musb->xceiv.state) { + case OTG_STATE_A_PERIPHERAL: + case OTG_STATE_A_WAIT_VFALL: + DBG(1, "HNP: Switching back to A-host\n"); + musb_g_disconnect(musb); + musb_root_disconnect(musb); + musb->xceiv.state = OTG_STATE_A_IDLE; + musb->is_active = 0; + break; + case OTG_STATE_B_HOST: + DBG(1, "HNP: Disabling HR\n"); + hcd->self.is_b_host = 0; + musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + reg = musb_readb(pBase, MGC_O_HDRC_POWER); + reg |= MGC_M_POWER_SUSPENDM; + musb_writeb(pBase, MGC_O_HDRC_POWER, reg); + /* REVISIT: Start SESSION_REQUEST here? */ + break; + default: + DBG(1, "HNP: Stopping in unknown state %s\n", + otg_state_string(musb)); + } +} + +#endif + /* * Interrupt Service Routine to record USB "global" interrupts. * Since these do not happen often and signify things of @@ -519,12 +582,16 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB, /* indicate new connection to OTG machine */ switch (pThis->xceiv.state) { case OTG_STATE_B_WAIT_ACON: + DBG(1, "HNP: Waiting to switch to b_host state\n"); pThis->xceiv.state = OTG_STATE_B_HOST; + hcd->self.is_b_host = 1; break; default: if ((devctl & MGC_M_DEVCTL_VBUS) - == (3 << MGC_S_DEVCTL_VBUS)) + == (3 << MGC_S_DEVCTL_VBUS)) { pThis->xceiv.state = OTG_STATE_A_HOST; + hcd->self.is_b_host = 0; + } break; } DBG(1, "CONNECT (%s) devctl %02x\n", @@ -633,11 +700,17 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: musb_root_disconnect(pThis); + if (pThis->a_wait_bcon != 0) + musb_platform_try_idle(pThis, jiffies + + msecs_to_jiffies(pThis->a_wait_bcon)); break; #endif /* HOST */ #ifdef CONFIG_USB_MUSB_OTG - case OTG_STATE_A_PERIPHERAL: case OTG_STATE_B_HOST: + musb_hnp_stop(pThis); + break; + /* FALLTHROUGH */ + case OTG_STATE_A_PERIPHERAL: musb_root_disconnect(pThis); /* FALLTHROUGH */ case OTG_STATE_B_WAIT_ACON: @@ -662,20 +735,37 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, handled = IRQ_HANDLED; switch (pThis->xceiv.state) { + case OTG_STATE_A_PERIPHERAL: + musb_hnp_stop(pThis); + break; case OTG_STATE_B_PERIPHERAL: musb_g_suspend(pThis); pThis->is_active = is_otg_enabled(pThis) && pThis->xceiv.gadget->b_hnp_enable; if (pThis->is_active) { pThis->xceiv.state = OTG_STATE_B_WAIT_ACON; - /* REVISIT timeout for b_ase0_brst, etc */ +#ifdef CONFIG_USB_MUSB_OTG + DBG(1, "HNP: Setting timer for b_ase0_brst\n"); + musb_otg_timer.data = (unsigned long)pThis; + mod_timer(&musb_otg_timer, jiffies + + msecs_to_jiffies(TB_ASE0_BRST)); +#endif } break; + case OTG_STATE_A_WAIT_BCON: + if (pThis->a_wait_bcon != 0) + musb_platform_try_idle(pThis, jiffies + + msecs_to_jiffies(pThis->a_wait_bcon)); + break; case OTG_STATE_A_HOST: pThis->xceiv.state = OTG_STATE_A_SUSPEND; pThis->is_active = is_otg_enabled(pThis) && pThis->xceiv.host->b_hnp_enable; break; + case OTG_STATE_B_HOST: + /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ + DBG(1, "REVISIT: SUSPEND as B_HOST\n"); + break; default: /* "should not happen" */ pThis->is_active = 0; @@ -783,7 +873,7 @@ void musb_stop(struct musb *musb) * OTG mode, gadget driver module rmmod/modprobe cycles that * - ... */ - musb_platform_try_idle(musb); + musb_platform_try_idle(musb, 0); } static void musb_shutdown(struct platform_device *pdev) @@ -1567,13 +1657,72 @@ musb_cable_show(struct device *dev, struct device_attribute *attr, char *buf) } else /* VBUS level below A-Valid */ v2 = "disconnected"; #endif - musb_platform_try_idle(musb); + musb_platform_try_idle(musb, 0); spin_unlock_irqrestore(&musb->Lock, flags); return sprintf(buf, "%s%s\n", v1, v2); } static DEVICE_ATTR(cable, S_IRUGO, musb_cable_show, NULL); +static ssize_t +musb_vbus_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + unsigned long val; + + spin_lock_irqsave(&musb->Lock, flags); + if (sscanf(buf, "%lu", &val) < 1) { + printk(KERN_ERR "Invalid VBUS timeout ms value\n"); + return -EINVAL; + } + musb->a_wait_bcon = val; + if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON) + musb->is_active = 0; + musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val)); + spin_unlock_irqrestore(&musb->Lock, flags); + + return n; +} + +static ssize_t +musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; + unsigned long val; + + spin_lock_irqsave(&musb->Lock, flags); + val = musb->a_wait_bcon; + spin_unlock_irqrestore(&musb->Lock, flags); + + return sprintf(buf, "%lu\n", val); +} +static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store); + +static ssize_t +musb_srp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct musb *musb=dev_to_musb(dev); + unsigned long flags; + unsigned short srp; + + if (sscanf(buf, "%hu", &srp) != 1 + || (srp != 1)) { + printk (KERN_ERR "SRP: Value must be 1\n"); + return -EINVAL; + } + + spin_lock_irqsave(&musb->Lock, flags); + if (srp == 1) + musb_g_wakeup(musb); + spin_unlock_irqrestore(&musb->Lock, flags); + + return n; +} +static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store); #endif /* Only used to provide cable state change events */ @@ -1643,6 +1792,10 @@ static void musb_free(struct musb *musb) #ifdef CONFIG_SYSFS device_remove_file(musb->controller, &dev_attr_mode); device_remove_file(musb->controller, &dev_attr_cable); + device_remove_file(musb->controller, &dev_attr_vbus); +#ifdef CONFIG_USB_MUSB_OTG + device_remove_file(musb->controller, &dev_attr_srp); +#endif #endif #ifdef CONFIG_USB_GADGET_MUSB_HDRC @@ -1871,6 +2024,10 @@ fail: #ifdef CONFIG_SYSFS status = device_create_file(dev, &dev_attr_mode); status = device_create_file(dev, &dev_attr_cable); + status = device_create_file(dev, &dev_attr_vbus); +#ifdef CONFIG_USB_MUSB_OTG + status = device_create_file(dev, &dev_attr_srp); +#endif /* CONFIG_USB_MUSB_OTG */ status = 0; #endif diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index f3028a469f8..ddeca4a3a61 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -25,6 +25,8 @@ #include "musbdefs.h" +static void tusb_source_power(struct musb *musb, int is_on); + /* * Checks the revision. We need to use the DMA register as 3.0 does not * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV. @@ -71,7 +73,7 @@ static void tusb_wbus_quirk(struct musb *musb, int enabled) { void __iomem *base = musb->ctrl_base; static u32 phy_otg_ctrl = 0, phy_otg_ena = 0; - u32 int_src, tmp; + u32 tmp; if (enabled) { phy_otg_ctrl = musb_readl(base, TUSB_PHY_OTG_CTRL); @@ -321,7 +323,7 @@ static void tusb_set_clock_source(struct musb *musb, unsigned mode) * USB link is not suspended ... and tells us the relevant wakeup * events. SW_EN for voltage is handled separately. */ -static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) +void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) { void __iomem *base = musb->ctrl_base; u32 reg; @@ -393,6 +395,23 @@ static void musb_do_idle(unsigned long _musb) unsigned long flags; spin_lock_irqsave(&musb->Lock, flags); + + switch (musb->xceiv.state) { + case OTG_STATE_A_WAIT_BCON: + if ((musb->a_wait_bcon != 0) + && (musb->idle_timeout == 0 + || time_after(jiffies, musb->idle_timeout))) { + DBG(4, "Nothing connected %s, turning off VBUS\n", + otg_state_string(musb)); + tusb_source_power(musb, 0); + musb->xceiv.state = OTG_STATE_A_IDLE; + musb->is_active = 0; + } + break; + default: + break; + } + if (!musb->is_active) { u32 wakeups; @@ -434,12 +453,35 @@ done: * we don't want to treat that full speed J as a wakeup event. * ... peripherals must draw only suspend current after 10 msec. */ -void musb_platform_try_idle(struct musb *musb) +void musb_platform_try_idle(struct musb *musb, unsigned long timeout) { - if (musb->is_active) + unsigned long default_timeout = jiffies + msecs_to_jiffies(3); + static unsigned long last_timer = 0; + + if (timeout == 0) + timeout = default_timeout; + + if (musb->is_active) { + DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); del_timer(&musb_idle_timer); - else - mod_timer(&musb_idle_timer, jiffies + msecs_to_jiffies(3)); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout)) { + if (!timer_pending(&musb_idle_timer)) + last_timer = timeout; + else { + DBG(4, "Longer idle timer already pending, ignoring\n"); + return; + } + } + last_timer = timeout; + + DBG(4, "%s inactive, for idle timer for %lu ms\n", + otg_state_string(musb), + (unsigned long)jiffies_to_msecs(timeout - jiffies)); + mod_timer(&musb_idle_timer, timeout); } /* ticks of 60 MHz clock */ @@ -467,7 +509,6 @@ static void tusb_source_power(struct musb *musb, int is_on) if (is_on) { musb->is_active = 1; 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; @@ -575,10 +616,11 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) "otg_stat: %08x\n", otg_stat); } -static inline void +static inline unsigned long tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) { - u32 otg_stat = musb_readl(base, TUSB_DEV_OTG_STAT); + u32 otg_stat = musb_readl(base, TUSB_DEV_OTG_STAT); + unsigned long idle_timeout = 0; /* ID pin */ if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) { @@ -591,6 +633,10 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) DBG(2, "Default-%c\n", default_a ? 'A' : 'B'); musb->xceiv.default_a = default_a; tusb_source_power(musb, default_a); + + /* Don't allow idling immediately */ + if (default_a) + idle_timeout = jiffies + (HZ * 3); } /* VBUS state change */ @@ -621,13 +667,34 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) } DBG(2, "vbus change, %s, otg %03x\n", otg_state_string(musb), otg_stat); + idle_timeout = jiffies + (1 * HZ); schedule_work(&musb->irq_work); } else /* A-dev state machine */ { + u8 devctl; + DBG(2, "vbus change, %s, otg %03x\n", otg_state_string(musb), otg_stat); switch (musb->xceiv.state) { + case OTG_STATE_A_IDLE: + DBG(2, "Got SRP, turning on VBUS\n"); + devctl = musb_readb(musb->pRegs, + MGC_O_HDRC_DEVCTL); + devctl |= MGC_M_DEVCTL_SESSION; + musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, + devctl); + musb->xceiv.state = OTG_STATE_A_WAIT_VRISE; + + /* CONNECT can wake if a_wait_bcon is set */ + if (musb->a_wait_bcon != 0) + musb->is_active = 0; + else + musb->is_active = 1; + + idle_timeout = jiffies + + msecs_to_jiffies(musb->a_wait_bcon); + break; case OTG_STATE_A_WAIT_VRISE: /* ignore; A-session-valid < VBUS_VALID/2, * we monitor this with the timer @@ -661,25 +728,19 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) */ 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; - - /* REVISIT: if nothing is connected yet, - * mark controller as inactive so that - * we can suspend the TUSB chip. - */ - - /* timeout 0 == infinite (like non-OTG hosts) */ - timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_BCON); - if (timer) - musb_writel(base, TUSB_DEV_OTG_TIMER, - timer); + /* CONNECT can wake if a_wait_bcon is set */ + if (musb->a_wait_bcon != 0) + musb->is_active = 0; + else + musb->is_active = 1; + idle_timeout = jiffies + + msecs_to_jiffies(musb->a_wait_bcon); } else { /* REVISIT report overcurrent to hub? */ ERR("vbus too slow, devctl %02x\n", devctl); @@ -687,8 +748,9 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) } break; case OTG_STATE_A_WAIT_BCON: - if (OTG_TIME_A_WAIT_BCON) - tusb_source_power(musb, 0); + if (musb->a_wait_bcon != 0) + idle_timeout = jiffies + + msecs_to_jiffies(musb->a_wait_bcon); break; case OTG_STATE_A_SUSPEND: break; @@ -698,13 +760,15 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) break; } } + + return idle_timeout; } static irqreturn_t tusb_interrupt(int irq, void *__hci) { struct musb *musb = __hci; void __iomem *base = musb->ctrl_base; - unsigned long flags; + unsigned long flags, idle_timeout = 0; u32 int_mask, int_src; spin_lock_irqsave(&musb->Lock, flags); @@ -753,11 +817,14 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci) // REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS } + if (int_src & TUSB_INT_SRC_USB_IP_CONN) + del_timer(&musb_idle_timer); + /* OTG state change reports (annoyingly) not issued by Mentor core */ if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG | TUSB_INT_SRC_OTG_TIMEOUT | TUSB_INT_SRC_ID_STATUS_CHNG)) - tusb_otg_ints(musb, int_src, base); + idle_timeout = tusb_otg_ints(musb, int_src, base); /* TX dma callback must be handled here, RX dma callback is * handled in tusb_omap_dma_cb. @@ -799,7 +866,7 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci) musb_writel(base, TUSB_INT_SRC_CLEAR, int_src & ~TUSB_INT_MASK_RESERVED_BITS); - musb_platform_try_idle(musb); + musb_platform_try_idle(musb, idle_timeout); musb_writel(base, TUSB_INT_MASK, int_mask); spin_unlock_irqrestore(&musb->Lock, flags); diff --git a/drivers/usb/musb/virthub.c b/drivers/usb/musb/virthub.c index 6a65f4fd1c0..5b1163b031c 100644 --- a/drivers/usb/musb/virthub.c +++ b/drivers/usb/musb/virthub.c @@ -61,10 +61,20 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend) */ power = musb_readb(pBase, MGC_O_HDRC_POWER); if (bSuspend) { + int retries = 10000; + power &= ~MGC_M_POWER_RESUME; power |= MGC_M_POWER_SUSPENDM; musb_writeb(pBase, MGC_O_HDRC_POWER, power); + /* Needed for OPT A tests */ + power = musb_readb(pBase, MGC_O_HDRC_POWER); + while (power & MGC_M_POWER_SUSPENDM) { + power = musb_readb(pBase, MGC_O_HDRC_POWER); + if (retries-- < 1) + break; + } + DBG(3, "Root port suspended, power %02x\n", power); musb->port1_status |= USB_PORT_STAT_SUSPEND; @@ -73,7 +83,7 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend) 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); + musb_platform_try_idle(musb, 0); break; case OTG_STATE_B_HOST: musb->xceiv.state = OTG_STATE_B_PERIPHERAL; @@ -362,7 +372,11 @@ int musb_hub_control( temp = MGC_M_TEST_FORCE_HOST | MGC_M_TEST_FORCE_HS; - /* FIXME and enable a session too */ + musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, MGC_M_DEVCTL_SESSION); + break; + case 6: + pr_debug("TEST_FIFO_ACCESS\n"); + temp = MGC_M_TEST_FIFO_ACCESS; break; default: goto error;