Commit 7f15a092 authored by Tony Lindgren's avatar Tony Lindgren

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: default avatarTony Lindgren <tony@atomide.com>
parent 36c8d657
......@@ -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)
......
......@@ -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
......
......@@ -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;
......
......@@ -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
......
......@@ -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
......
......@@ -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);
......
......@@ -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;
......
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