From: David Brownell Date: Thu, 14 Sep 2006 15:08:32 +0000 (+0300) Subject: MUSB: More TUSB OTG support X-Git-Tag: v2.6.18-omap1~63^2^2~1 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=e3071039a1389fa767113ef4472040df507f3b02;p=linux-2.6-omap-h63xx.git MUSB: More TUSB OTG support 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 --- diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6aac44d3d22..253a46f7c21 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1598,8 +1598,6 @@ init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 bEnd, int is_in) 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); } /* @@ -1634,8 +1632,6 @@ static inline void __devinit musb_g_init_endpoints(struct musb *pThis) } } } - 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 @@ -1656,10 +1652,6 @@ int __devinit musb_gadget_setup(struct musb *pThis) 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"); @@ -1762,11 +1754,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) * 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 @@ -1784,7 +1777,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) spin_unlock_irqrestore(&pThis->Lock, flags); } } -#endif } return retval; @@ -1872,15 +1864,14 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) 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); @@ -2002,13 +1993,16 @@ __acquires(pThis->Lock) 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 @@ -2016,5 +2010,5 @@ __acquires(pThis->Lock) /* 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); } diff --git a/drivers/usb/musb/musbdefs.h b/drivers/usb/musb/musbdefs.h index 0319bd5388e..5f1375c56f8 100644 --- a/drivers/usb/musb/musbdefs.h +++ b/drivers/usb/musb/musbdefs.h @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -67,13 +69,17 @@ struct musb_ep; #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) @@ -82,15 +88,8 @@ struct musb_ep; /* 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() @@ -101,6 +100,15 @@ struct musb_ep; #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 @@ -111,9 +119,6 @@ struct musb_ep; #ifdef CONFIG_USB_GADGET_MUSB_HDRC -#include -#include "musb_gadget.h" - #define is_peripheral_capable() (1) extern irqreturn_t musb_g_ep0_irq(struct musb *); @@ -129,8 +134,6 @@ extern void musb_g_disconnect(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) {} @@ -142,10 +145,6 @@ static inline void musb_g_disconnect(struct musb *m) {} #ifdef CONFIG_USB_MUSB_HDRC_HCD -#include -#include "../core/hcd.h" -#include "musb_host.h" - #define is_host_capable() (1) extern irqreturn_t musb_h_ep0_irq(struct musb *); @@ -160,8 +159,6 @@ static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; } 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 diff --git a/drivers/usb/musb/otg.h b/drivers/usb/musb/otg.h index a62961bffe6..cbea5d7b2e6 100644 --- a/drivers/usb/musb/otg.h +++ b/drivers/usb/musb/otg.h @@ -55,6 +55,9 @@ * Finally, it provides the necessary bus control service. */ +/* sysfs flag to seletively force peripheral-only operation */ +extern int musb_otg; + /****************************** CONSTANTS ********************************/ /* diff --git a/drivers/usb/musb/plat_uds.c b/drivers/usb/musb/plat_uds.c index b7318faeaa1..93bfa0bb105 100644 --- a/drivers/usb/musb/plat_uds.c +++ b/drivers/usb/musb/plat_uds.c @@ -171,6 +171,19 @@ MODULE_LICENSE("GPL"); /*-------------------------------------------------------------------------*/ +#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 @@ -629,12 +642,8 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, /* 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; @@ -648,39 +657,44 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, /* * 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; } } @@ -958,7 +972,7 @@ static const struct fifo_cfg __devinitdata ep0_cfg = { 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; @@ -995,13 +1009,13 @@ static int __devinit ep_config_from_table(struct musb *musb) 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) { @@ -1015,7 +1029,7 @@ static int __devinit ep_config_from_table(struct musb *musb) 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 @@ -1346,10 +1360,13 @@ irqreturn_t musb_interrupt(struct musb *musb) // 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; @@ -1364,10 +1381,13 @@ irqreturn_t musb_interrupt(struct musb *musb) // 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++; @@ -1406,16 +1426,22 @@ void musb_dma_completion(struct musb *musb, u8 bLocalEnd, u8 bTransmit) } 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); + } } } } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index bf16d2d510d..f436e5332ce 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -171,7 +171,7 @@ static int tusb_set_power(struct otg_transceiver *x, unsigned mA) 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; } @@ -209,20 +209,21 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) 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); @@ -344,8 +345,10 @@ static void tusb_set_vbus(struct musb *musb, int is_on) 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); @@ -355,7 +358,7 @@ static void tusb_set_vbus(struct musb *musb, int is_on) 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); @@ -371,13 +374,12 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) 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 */ @@ -387,21 +389,16 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) 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 */ { @@ -432,16 +429,15 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) /* 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); @@ -463,7 +459,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) 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; @@ -487,17 +483,15 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r) 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) { @@ -540,11 +534,13 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r) /* 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; @@ -568,10 +564,8 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r) } 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, @@ -619,6 +613,9 @@ void musb_platform_enable(struct musb * musb) 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__); diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h index cf3b250f51b..a44a4b3479e 100644 --- a/drivers/usb/musb/tusb6010.h +++ b/drivers/usb/musb/tusb6010.h @@ -75,6 +75,7 @@ #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) diff --git a/drivers/usb/musb/virthub.c b/drivers/usb/musb/virthub.c index 63f31fd1cd5..a1c7152bb31 100644 --- a/drivers/usb/musb/virthub.c +++ b/drivers/usb/musb/virthub.c @@ -51,6 +51,9 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend) u8 power; void __iomem *pBase = musb->pRegs; + if (!is_host_active(musb)) + return; + power = musb_readb(pBase, MGC_O_HDRC_POWER); if (bSuspend) { @@ -181,12 +184,11 @@ int musb_hub_control( 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); @@ -279,7 +281,8 @@ int musb_hub_control( * 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); @@ -288,6 +291,9 @@ int musb_hub_control( 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: