Add more host/OTG functionality.
New Features:
- Cable based role switching (using the ID pin) mostly works. This gives
much of the end-user value for USB OTG; there's also dynamic role
switching (using special HNP protocol with another OTG device), which
surely won't work yet.
- Root can write 0/N to /sys/module/musb_hdrc/parameters/musb_otg to
disable OTG functionality, forcing B-Default mode (with optional SRP
support, but without 8mA current limits) when it's set. Change this
before the gadget driver initializes (e.g. before "modprobe").
Bugfixes:
- Fix many OTG mode startup glitches, notably for the root hub.
- Add workaround for "issue 4".
Cleanups:
- Remove more mode-specific #ifdeffery
- ReMoveSomeCamelCasing
- Quieter startup
- Quash another Kconfig rebellion
- Add missing bit declaration
- Tersify TUSB irq messages
Open Issues:
- Nothing passes remote wakeup down to root hub (unchanged)
- Strange VBUS errors (unchanged; workaround: use hub modified for
N770, providing 5V supply till TUSB board does so reliably);
- Plug in/out as peripheral many times; fine. As host (with modified
hub), ditto. Now peripheral again ... fails.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
ep->end_point.ops = &musb_ep_ops;
list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list);
}
- DBG(4, "periph: %s, maxpacket %d\n", ep->end_point.name,
- ep->end_point.maxpacket);
}
/*
}
}
}
- DBG(2, "initialized %d (max %d) endpoints\n", count,
- MUSB_C_NUM_EPS * 2 - 1);
}
/* called once during driver setup to initialize and link into
pThis->g.ops = &musb_gadget_operations;
pThis->g.is_dualspeed = 1;
pThis->g.speed = USB_SPEED_UNKNOWN;
-#ifdef CONFIG_USB_MUSB_OTG
- if (pThis->board_mode == MUSB_OTG)
- pThis->g.is_otg = 1;
-#endif
/* this "gadget" abstracts/virtualizes the controller */
strcpy(pThis->g.dev.bus_id, "gadget");
* hosts only see fully functional devices.
*/
- musb_start(pThis);
+ if (!is_otg_enabled(pThis))
+ musb_start(pThis);
+
spin_unlock_irqrestore(&pThis->Lock, flags);
-#ifdef CONFIG_USB_MUSB_OTG
- if (pThis->board_mode == MUSB_OTG) {
+ if (is_otg_enabled(pThis)) {
DBG(3, "OTG startup...\n");
/* REVISIT: funcall to other code, which also
spin_unlock_irqrestore(&pThis->Lock, flags);
}
}
-#endif
}
return retval;
retval = -EINVAL;
spin_unlock_irqrestore(&musb->Lock, flags);
-#ifdef CONFIG_USB_MUSB_OTG
- if (retval == 0 && musb->board_mode == MUSB_OTG) {
+ if (is_otg_enabled(musb) && retval == 0) {
usb_remove_hcd(musb_to_hcd(musb));
/* FIXME we need to be able to register another
* gadget driver here and have everything work;
* that currently misbehaves.
*/
}
-#endif
+
return retval;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
pThis->g.a_alt_hnp_support = 0;
pThis->g.a_hnp_support = 0;
+ if (is_otg_enabled(pThis))
+ pThis->g.is_otg = !!musb_otg;
+
/* Normal reset, as B-Device;
* or else after HNP, as A-Device
*/
if (devctl & MGC_M_DEVCTL_BDEVICE) {
pThis->xceiv.state = OTG_STATE_B_PERIPHERAL;
pThis->g.is_a_peripheral = 0;
- } else if (is_otg_enabled(pThis) && pThis->board_mode == MUSB_OTG) {
+ } else if (is_otg_enabled(pThis) && musb_otg) {
pThis->xceiv.state = OTG_STATE_A_PERIPHERAL;
pThis->g.is_a_peripheral = 1;
} else
/* start with default limits on VBUS power draw */
(void) musb_gadget_vbus_draw(&pThis->g,
- is_otg_enabled(pThis) ? 8 : 100);
+ (is_otg_enabled(pThis) && musb_otg) ? 8 : 100);
}
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb.h>
#include <linux/usb_otg.h>
#include <linux/usb/musb.h>
#include "plat_arc.h"
#include "musbhdrc.h"
+#include "musb_gadget.h"
+#include "../core/hcd.h"
+#include "musb_host.h"
+#include "otg.h"
+
/* REVISIT tune this */
#define MIN_DMA_REQUEST 1 /* use PIO below this xfer size */
#ifdef CONFIG_USB_MUSB_OTG
-#include "otg.h"
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
/* NOTE: otg and peripheral-only state machines start at B_IDLE.
* OTG or host-only go to A_IDLE when ID is sensed.
*/
-#define is_peripheral_active(m) (is_peripheral_capable() && !(m)->bIsHost)
-#define is_host_active(m) (is_host_capable() && (m)->bIsHost)
-
-/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't really
- * override that choice selection (often USB_GADGET_DUMMY_HCD).
- */
-#ifndef CONFIG_USB_GADGET_MUSB_HDRC
-#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
-#endif
+#define is_peripheral_active(m) (!(m)->bIsHost)
+#define is_host_active(m) ((m)->bIsHost)
#else
#define is_peripheral_enabled(musb) is_peripheral_capable()
#define is_host_active(musb) is_host_capable()
#endif
+#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL)
+/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always
+ * override that choice selection (often USB_GADGET_DUMMY_HCD).
+ */
+#ifndef CONFIG_USB_GADGET_MUSB_HDRC
+#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
+#endif
+#endif /* need MUSB gadget selection */
+
#ifdef CONFIG_PROC_FS
#include <linux/fs.h>
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-#include <linux/usb_gadget.h>
-#include "musb_gadget.h"
-
#define is_peripheral_capable() (1)
extern irqreturn_t musb_g_ep0_irq(struct musb *);
#define is_peripheral_capable() (0)
static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
-static inline void musb_g_tx(struct musb *m, u8 e) {}
-static inline void musb_g_rx(struct musb *m, u8 e) {}
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) {}
#ifdef CONFIG_USB_MUSB_HDRC_HCD
-#include <linux/usb.h>
-#include "../core/hcd.h"
-#include "musb_host.h"
-
#define is_host_capable() (1)
extern irqreturn_t musb_h_ep0_irq(struct musb *);
static inline void musb_host_tx(struct musb *m, u8 e) {}
static inline void musb_host_rx(struct musb *m, u8 e) {}
-static inline void musb_root_disconnect(struct musb *musb) { BUG(); }
-
#endif
* Finally, it provides the necessary bus control service.
*/
+/* sysfs flag to seletively force peripheral-only operation */
+extern int musb_otg;
+
/****************************** CONSTANTS ********************************/
/*
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_MUSB_OTG
+
+/* For debugging/prototyping: allow disabling host side support on boards
+ * with Mini-AB (or Mini-A) connectors, making peripheral side support look
+ * like pure peripherals (not reporting OTG capabilities, and able to
+ * draw a full 100mA unit load).
+ */
+int musb_otg = 1;
+
+module_param(musb_otg, bool, 0600);
+MODULE_PARM_DESC(musb_otg, "enable/disable OTG capabilities");
+#endif
+
static inline struct musb *dev_to_musb(struct device *dev)
{
#ifdef CONFIG_USB_MUSB_HDRC_HCD
/* peripheral suspend, may trigger HNP */
if (!(devctl & MGC_M_DEVCTL_HM)) {
musb_g_suspend(pThis);
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
pThis->is_active = is_otg_enabled(pThis)
&& pThis->xceiv.gadget->b_hnp_enable;
-#else
- pThis->is_active = 0;
-#endif
otg_input_changed(pThis, devctl, FALSE, FALSE, TRUE);
} else
pThis->is_active = 0;
/*
* Program the HDRC to start (enable interrupts, dma, etc.).
*/
-void musb_start(struct musb * pThis)
+void musb_start(struct musb *musb)
{
- void __iomem *pBase = pThis->pRegs;
- u8 state;
+ void __iomem *regs = musb->pRegs;
+ u8 devctl;
DBG(2, "<==\n");
/* TODO: always set ISOUPDATE in POWER (periph mode) and leave it on! */
/* Set INT enable registers, enable interrupts */
- musb_writew(pBase, MGC_O_HDRC_INTRTXE, pThis->wEndMask);
- musb_writew(pBase, MGC_O_HDRC_INTRRXE, pThis->wEndMask & 0xfffe);
- musb_writeb(pBase, MGC_O_HDRC_INTRUSBE, 0xf7);
+ musb_writew(regs, MGC_O_HDRC_INTRTXE, musb->wEndMask);
+ musb_writew(regs, MGC_O_HDRC_INTRRXE, musb->wEndMask & 0xfffe);
+ musb_writeb(regs, MGC_O_HDRC_INTRUSBE, 0xf7);
- musb_writeb(pBase, MGC_O_HDRC_TESTMODE, 0);
+ musb_writeb(regs, MGC_O_HDRC_TESTMODE, 0);
- musb_platform_enable(pThis);
+ musb_platform_enable(musb);
/* enable high-speed/low-power and start session */
- musb_writeb(pBase, MGC_O_HDRC_POWER,
+ musb_writeb(regs, MGC_O_HDRC_POWER,
MGC_M_POWER_SOFTCONN | MGC_M_POWER_HSENAB);
- switch (pThis->board_mode) {
+ musb->is_active = 0;
+ switch (musb->board_mode) {
case MUSB_HOST:
- musb_set_vbus(pThis, 1);
+ musb_set_vbus(musb, 1);
break;
case MUSB_OTG:
- WARN("how to start OTG session?\n");
+ /* session started after:
+ * (a) ID-grounded irq, host mode;
+ * (b) vbus present/connect IRQ, peripheral mode;
+ * (c) peripheral initiates, using SRP
+ */
break;
case MUSB_PERIPHERAL:
- state = musb_readb(pBase, MGC_O_HDRC_DEVCTL);
- musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
- state & ~MGC_M_DEVCTL_SESSION);
+ devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL);
+ musb_writeb(regs, MGC_O_HDRC_DEVCTL,
+ devctl & ~MGC_M_DEVCTL_SESSION);
break;
}
}
static int __devinit ep_config_from_table(struct musb *musb)
{
const struct fifo_cfg *cfg;
- unsigned n;
+ unsigned i, n;
int offset;
struct musb_hw_ep *hw_ep = musb->aLocalEnd;
offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0);
// assert(offset > 0)
- while (n--) {
+ for (i = 0; i < n; i++) {
u8 epn = cfg->hw_ep_num;
if (epn >= MUSB_C_NUM_EPS) {
pr_debug( "%s: invalid ep %d\n",
musb_driver_name, epn);
- return -EINVAL;
+ continue;
}
offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
if (offset < 0) {
printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n",
musb_driver_name,
- musb->bEndCount, MUSB_C_NUM_EPS * 2 - 1,
+ n + 1, MUSB_C_NUM_EPS * 2 - 1,
offset, DYN_FIFO_SIZE);
#ifdef CONFIG_USB_MUSB_HDRC_HCD
// MGC_SelectEnd(musb->pRegs, ep_num);
/* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MGC_M_DEVCTL_HM)
- musb_host_rx(musb, ep_num);
- else
- musb_g_rx(musb, ep_num);
+ if (devctl & MGC_M_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_rx(musb, ep_num);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_rx(musb, ep_num);
+ }
}
reg >>= 1;
// MGC_SelectEnd(musb->pRegs, ep_num);
/* REVISIT just retval |= ep->tx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MGC_M_DEVCTL_HM)
- musb_host_tx(musb, ep_num);
- else
- musb_g_tx(musb, ep_num);
+ if (devctl & MGC_M_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_tx(musb, ep_num);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_tx(musb, ep_num);
+ }
}
reg >>= 1;
ep_num++;
} else {
/* endpoints 1..15 */
if (bTransmit) {
- if (devctl & MGC_M_DEVCTL_HM)
- musb_host_tx(musb, bLocalEnd);
- else
- musb_g_tx(musb, bLocalEnd);
+ if (devctl & MGC_M_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_tx(musb, bLocalEnd);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_tx(musb, bLocalEnd);
+ }
} else {
/* receive */
- if (devctl & MGC_M_DEVCTL_HM)
- musb_host_rx(musb, bLocalEnd);
- else
- musb_g_rx(musb, bLocalEnd);
+ if (devctl & MGC_M_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_rx(musb, bLocalEnd);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_rx(musb, bLocalEnd);
+ }
}
}
}
reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
musb_writel(base, TUSB_PRCM_MNGMT, reg);
- DBG(3, "draw max %d mA VBUS\n", mA);
+ DBG(2, "draw max %d mA VBUS\n", mA);
return 0;
}
wakeup_enables |= TUSB_PRCM_WNORCS;
musb_writel(base, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables);
-// FIXME issue 4, when host (driving vbus), enable hipower comparator
-
/* REVISIT writeup of WLD implies that if WLD set and ID is grounded,
* TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared.
* Presumably that's mostly to save power, hence WLD is immaterial ...
*/
reg = musb_readl(base, TUSB_PRCM_MNGMT);
- /* issue 4: when driving vbus, leave hipower comparator active */
- if (!is_host_active(musb))
+ /* issue 4: when driving vbus, use hipower (vbus_det) comparator */
+ if (is_host_active(musb)) {
+ reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
+ reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
+ } else {
+ reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
- reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN
- | TUSB_PRCM_MNGMT_PM_IDLE
- | TUSB_PRCM_MNGMT_DEV_IDLE;
+ }
+ reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE;
musb_writel(base, TUSB_PRCM_MNGMT, reg);
DBG(2, "idle, wake on %02x\n", wakeup_enables);
if (musb->xceiv.default_a) {
musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
devctl &= ~MGC_M_DEVCTL_SESSION;
- } else
+ } else {
+ musb->xceiv.state = OTG_STATE_B_IDLE;
musb->is_active = 0;
+ }
}
prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, devctl);
DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n",
- is_on ? "on" : "off",
+ otg_state_string(musb),
musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL),
musb_readl(base, TUSB_DEV_OTG_STAT),
conf, prcm);
int default_a;
if (is_otg_enabled(musb))
- default_a = !!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
+ default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
else
default_a = is_host_enabled(musb);
- if (default_a != musb->xceiv.default_a) {
- musb->xceiv.default_a = default_a;
- tusb_set_vbus(musb, default_a);
- }
+ DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
+ musb->xceiv.default_a = default_a;
+ tusb_set_vbus(musb, default_a);
}
/* VBUS state change */
if ((is_otg_enabled(musb) && !musb->xceiv.default_a)
|| !is_host_enabled(musb)) {
- /* REVISIT use the b_sess_valid comparator, not
- * lowpower one; TUSB_DEV_OTG_STAT_SESS_VALID ?
- */
-
- if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_SENSE) {
- musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
- musb->is_active = 1;
- /* REVISIT start the session? */
- } else {
- musb->xceiv.state = OTG_STATE_B_IDLE;
+ if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
+ if (musb->xceiv.state != OTG_STATE_B_IDLE) {
+ /* INTR_DISCONNECT can hide... */
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ musb->int_usb |= MGC_M_INTR_DISCONNECT;
+ }
musb->is_active = 0;
}
- DBG(1, "%s\n", musb->is_active
- ? "b_peripheral" : "b_idle");
-
+ DBG(2, "vbus change, %s, otg %03x\n",
+ otg_state_string(musb), otg_stat);
schedule_work(&musb->irq_work);
} else /* A-dev state machine */ {
/* OTG timer expiration */
if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) {
+ u8 devctl;
+
DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat);
switch (musb->xceiv.state) {
case OTG_STATE_A_WAIT_VRISE:
/* VBUS has probably been valid for a while now */
+ devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);
if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) {
- u8 devctl;
-
- devctl = musb_readb(musb->pRegs,
- MGC_O_HDRC_DEVCTL);
if ((devctl & MGC_M_DEVCTL_VBUS)
!= MGC_M_DEVCTL_VBUS) {
DBG(2, "devctl %02x\n", devctl);
else
musb_writel(base, TUSB_DEV_OTG_TIMER, 0);
} else {
- ERR("vbus rise time too slow\n");
+ ERR("vbus too slow, devctl %02x\n", devctl);
tusb_set_vbus(musb, 0);
}
break;
struct musb *musb = __hci;
void __iomem *base = musb->ctrl_base;
unsigned long flags;
- u32 dma_src = 0, int_src;
+ u32 int_src;
spin_lock_irqsave(&musb->Lock, flags);
int_src = musb_readl(base, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS;
- if (int_src & TUSB_INT_SRC_TXRX_DMA_DONE)
- dma_src = musb_readl(base, TUSB_DMA_INT_SRC);
-
- DBG(3, "TUSB interrupt dma: %08x int: %08x\n", dma_src, int_src);
+ DBG(3, "TUSB IRQ %08x\n", int_src);
musb->int_regs = r;
+ musb->int_usb = (u8) int_src;
/* Acknowledge wake-up source interrupts */
if (int_src & TUSB_INT_SRC_DEV_WAKEUP) {
/* TX dma callback must be handled here, RX dma callback is
* handled in tusb_omap_dma_cb.
*/
- if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE) && dma_src) {
- u32 real_dma_src = musb_readl(base, TUSB_DMA_INT_MASK);
+ if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) {
+ u32 dma_src = musb_readl(base, TUSB_DMA_INT_SRC);
+ u32 real_dma_src = musb_readl(base, TUSB_DMA_INT_MASK);
+ DBG(3, "DMA IRQ %08x\n", dma_src);
real_dma_src = ~real_dma_src & dma_src;
- if (tusb_dma_omap()) {
+ if (tusb_dma_omap() && real_dma_src) {
int tx_source = (real_dma_src & 0xffff);
int i;
} else
musb->int_rx = musb->int_tx = 0;
- if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff)) {
- musb->int_usb = (u8) int_src;
+ if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff))
musb_interrupt(musb);
- }
/* Acknowledge TUSB interrupts. Clear only non-reserved bits */
musb_writel(base, TUSB_INT_SRC_CLEAR,
set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW);
+ /* kickstart: force into the correct OTG state machine */
+ musb_writel(base, TUSB_INT_SRC_SET, TUSB_INT_SRC_ID_STATUS_CHNG);
+
if (is_dma_capable() && dma_off)
printk(KERN_WARNING "%s %s: dma not reactivated\n",
__FILE__, __FUNCTION__);
#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5)
#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4)
#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3)
+#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2)
#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0)
#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1)
#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0)
u8 power;
void __iomem *pBase = musb->pRegs;
+ if (!is_host_active(musb))
+ return;
+
power = musb_readb(pBase, MGC_O_HDRC_POWER);
if (bSuspend) {
int retval = 0;
unsigned long flags;
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)
- || !is_host_active(musb)))
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
return -ESHUTDOWN;
/* hub features: always zero, setting is a NOP
- * port features: reported, sometimes updated
+ * port features: reported, sometimes updated when host is active
* no indicators
*/
spin_lock_irqsave(&musb->Lock, flags);
* initialization logic, e.g. for OTG, or change any
* logic relating to VBUS power-up.
*/
- musb_start(musb);
+ if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
+ musb_start(musb);
break;
case USB_PORT_FEAT_RESET:
musb_port_reset(musb, TRUE);
musb_port_suspend(musb, TRUE);
break;
case USB_PORT_FEAT_TEST:
+ if (unlikely(is_host_active(musb)))
+ goto error;
+
wIndex >>= 8;
switch (wIndex) {
case 1: