diff options
Diffstat (limited to 'drivers/usb/musb/musb_core.c')
-rw-r--r-- | drivers/usb/musb/musb_core.c | 228 |
1 files changed, 127 insertions, 101 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4000cf6d1e8..554a414f65d 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -112,6 +112,7 @@ #include "davinci.h" #endif +#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON) unsigned musb_debug; @@ -267,7 +268,7 @@ void musb_load_testpacket(struct musb *musb) const char *otg_state_string(struct musb *musb) { - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: return "a_idle"; case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; @@ -288,12 +289,6 @@ const char *otg_state_string(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) @@ -302,16 +297,18 @@ void musb_otg_timer_func(unsigned long data) unsigned long flags; spin_lock_irqsave(&musb->lock, flags); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n"); musb_g_disconnect(musb); - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 0; break; + case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n"); - musb_hnp_stop(musb); + DBG(1, "HNP: %s timeout\n", otg_state_string(musb)); + musb_set_vbus(musb, 0); + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb)); @@ -320,10 +317,8 @@ void musb_otg_timer_func(unsigned long data) 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. + * Stops the HNP transition. Caller must take care of locking. */ void musb_hnp_stop(struct musb *musb) { @@ -331,20 +326,17 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; - switch (musb->xceiv.state) { + DBG(1, "HNP: stop from %s\n", otg_state_string(musb)); + + switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: Switching back to A-host\n"); musb_g_disconnect(musb); - musb->xceiv.state = OTG_STATE_A_IDLE; - MUSB_HST_MODE(musb); - musb->is_active = 0; + DBG(1, "HNP: back to %s\n", otg_state_string(musb)); 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; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; MUSB_DEV_MODE(musb); reg = musb_readb(mbase, MUSB_POWER); reg |= MUSB_POWER_SUSPENDM; @@ -402,7 +394,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (devctl & MUSB_DEVCTL_HM) { #ifdef CONFIG_USB_MUSB_HDRC_HCD - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_SUSPEND: /* remote wakeup? later, GetPortStatus * will stop RESUME signaling @@ -425,12 +417,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb->rh_timer = jiffies + msecs_to_jiffies(20); - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; musb->is_active = 1; usb_hcd_resume_root_hub(musb_to_hcd(musb)); break; case OTG_STATE_B_WAIT_ACON: - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 1; MUSB_DEV_MODE(musb); break; @@ -441,11 +433,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } #endif } else { - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_MUSB_HDRC_HCD case OTG_STATE_A_SUSPEND: /* possibly DISCONNECT is upcoming */ - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; usb_hcd_resume_root_hub(musb_to_hcd(musb)); break; #endif @@ -490,7 +482,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); musb->ep0_stage = MUSB_EP0_START; - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); musb_set_vbus(musb, 1); @@ -516,7 +508,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, * REVISIT: do delays from lots of DEBUG_KERNEL checks * make trouble here, keeping VBUS < 4.4V ? */ - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_HOST: /* recovery is dicey once we've gotten past the * initial stages of enumeration, but if VBUS @@ -594,37 +586,40 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (devctl & MUSB_DEVCTL_LSDEV) musb->port1_status |= USB_PORT_STAT_LOW_SPEED; - if (hcd->status_urb) - usb_hcd_poll_rh_status(hcd); - else - usb_hcd_resume_root_hub(hcd); - - MUSB_HST_MODE(musb); - /* indicate new connection to OTG machine */ - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: if (int_usb & MUSB_INTR_SUSPEND) { DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); - musb->xceiv.state = OTG_STATE_B_HOST; - hcd->self.is_b_host = 1; int_usb &= ~MUSB_INTR_SUSPEND; + goto b_host; } else DBG(1, "CONNECT as b_peripheral???\n"); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: Waiting to switch to b_host state\n"); - musb->xceiv.state = OTG_STATE_B_HOST; + DBG(1, "HNP: CONNECT, now b_host\n"); +b_host: + musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); break; default: if ((devctl & MUSB_DEVCTL_VBUS) == (3 << MUSB_DEVCTL_VBUS_SHIFT)) { - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; hcd->self.is_b_host = 0; } break; } + + /* poke the root hub */ + MUSB_HST_MODE(musb); + if (hcd->status_urb) + usb_hcd_poll_rh_status(hcd); + else + usb_hcd_resume_root_hub(hcd); + DBG(1, "CONNECT (%s) devctl %02x\n", otg_state_string(musb), devctl); } @@ -650,7 +645,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } } else if (is_peripheral_capable()) { DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_OTG case OTG_STATE_A_SUSPEND: /* We need to ignore disconnect on suspend @@ -661,24 +656,27 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb_g_reset(musb); /* FALLTHROUGH */ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ - DBG(1, "HNP: Setting timer as %s\n", - otg_state_string(musb)); - musb_otg_timer.data = (unsigned long)musb; - mod_timer(&musb_otg_timer, jiffies - + msecs_to_jiffies(100)); + /* never use invalid T(a_wait_bcon) */ + DBG(1, "HNP: in %s, %d msec timeout\n", + otg_state_string(musb), + TA_WAIT_BCON(musb)); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; case OTG_STATE_A_PERIPHERAL: - musb_hnp_stop(musb); + musb->ignore_disconnect = 0; + del_timer(&musb->otg_timer); + musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON: DBG(1, "HNP: RESET (%s), to b_peripheral\n", otg_state_string(musb)); - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); break; #endif case OTG_STATE_B_IDLE: - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; /* FALLTHROUGH */ case OTG_STATE_B_PERIPHERAL: musb_g_reset(musb); @@ -763,7 +761,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, MUSB_MODE(musb), devctl); handled = IRQ_HANDLED; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_MUSB_HDRC_HCD case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: @@ -776,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, #endif /* HOST */ #ifdef CONFIG_USB_MUSB_OTG case OTG_STATE_B_HOST: - musb_hnp_stop(musb); + /* REVISIT this behaves for "real disconnect" + * cases; make sure the other transitions from + * from B_HOST act right too. The B_HOST code + * in hnp_stop() is currently not used... + */ + musb_root_disconnect(musb); + musb_to_hcd(musb)->self.is_b_host = 0; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + MUSB_DEV_MODE(musb); + musb_g_disconnect(musb); break; case OTG_STATE_A_PERIPHERAL: musb_hnp_stop(musb); @@ -805,26 +812,35 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, otg_state_string(musb), devctl, power); handled = IRQ_HANDLED; - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { #ifdef CONFIG_USB_MUSB_OTG case OTG_STATE_A_PERIPHERAL: - /* - * We cannot stop HNP here, devctl BDEVICE might be - * still set. + /* We also come here if the cable is removed, since + * this silicon doesn't report ID-no-longer-grounded. + * + * We depend on T(a_wait_bcon) to shut us down, and + * hope users don't do anything dicey during this + * undesired detour through A_WAIT_BCON. */ + musb_hnp_stop(musb); + usb_hcd_resume_root_hub(musb_to_hcd(musb)); + musb_root_disconnect(musb); + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon + ? : OTG_TIME_A_WAIT_BCON)); break; #endif case OTG_STATE_B_PERIPHERAL: musb_g_suspend(musb); musb->is_active = is_otg_enabled(musb) - && musb->xceiv.gadget->b_hnp_enable; + && musb->xceiv->gadget->b_hnp_enable; if (musb->is_active) { #ifdef CONFIG_USB_MUSB_OTG - musb->xceiv.state = OTG_STATE_B_WAIT_ACON; + musb->xceiv->state = OTG_STATE_B_WAIT_ACON; DBG(1, "HNP: Setting timer for b_ase0_brst\n"); - musb_otg_timer.data = (unsigned long)musb; - mod_timer(&musb_otg_timer, jiffies - + msecs_to_jiffies(TB_ASE0_BRST)); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies( + OTG_TIME_B_ASE0_BRST)); #endif } break; @@ -834,9 +850,9 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, + msecs_to_jiffies(musb->a_wait_bcon)); break; case OTG_STATE_A_HOST: - musb->xceiv.state = OTG_STATE_A_SUSPEND; + musb->xceiv->state = OTG_STATE_A_SUSPEND; musb->is_active = is_otg_enabled(musb) - && musb->xceiv.host->b_hnp_enable; + && musb->xceiv->host->b_hnp_enable; break; case OTG_STATE_B_HOST: /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ @@ -1068,14 +1084,13 @@ static struct fifo_cfg __initdata mode_4_cfg[] = { { .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 13, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 13, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, }, { .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, }, { .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, }, }; @@ -1335,11 +1350,11 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) } if (reg & MUSB_CONFIGDATA_HBRXE) { strcat(aInfo, ", HB-ISO Rx"); - strcat(aInfo, " (X)"); /* no driver support */ + musb->hb_iso_rx = true; } if (reg & MUSB_CONFIGDATA_HBTXE) { strcat(aInfo, ", HB-ISO Tx"); - strcat(aInfo, " (X)"); /* no driver support */ + musb->hb_iso_tx = true; } if (reg & MUSB_CONFIGDATA_SOFTCONE) strcat(aInfo, ", SoftConn"); @@ -1481,13 +1496,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci) spin_unlock_irqrestore(&musb->lock, flags); - /* REVISIT we sometimes get spurious IRQs on g_ep0 - * not clear why... - */ - if (retval != IRQ_HANDLED) - DBG(5, "spurious?\n"); - - return IRQ_HANDLED; + return retval; } #else @@ -1687,8 +1696,9 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr, } spin_lock_irqsave(&musb->lock, flags); - musb->a_wait_bcon = val; - if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON) + /* force T(a_wait_bcon) to be zero/unlimited *OR* valid */ + musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ; + 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); @@ -1706,10 +1716,13 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) spin_lock_irqsave(&musb->lock, flags); val = musb->a_wait_bcon; + /* FIXME get_vbus_status() is normally #defined as false... + * and is effectively TUSB-specific. + */ vbus = musb_platform_get_vbus_status(musb); spin_unlock_irqrestore(&musb->lock, flags); - return sprintf(buf, "Vbus %s, timeout %lu\n", + return sprintf(buf, "Vbus %s, timeout %lu msec\n", vbus ? "on" : "off", val); } static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store); @@ -1749,8 +1762,8 @@ static void musb_irq_work(struct work_struct *data) struct musb *musb = container_of(data, struct musb, irq_work); static int old_state; - if (musb->xceiv.state != old_state) { - old_state = musb->xceiv.state; + if (musb->xceiv->state != old_state) { + old_state = musb->xceiv->state; sysfs_notify(&musb->controller->kobj, NULL, "mode"); } } @@ -1782,6 +1795,7 @@ allocate_instance(struct device *dev, hcd->uses_new_polling = 1; musb->vbuserr_retry = VBUSERR_RETRY_COUNT; + musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON; #else musb = kzalloc(sizeof *musb, GFP_KERNEL); if (!musb) @@ -1847,7 +1861,7 @@ static void musb_free(struct musb *musb) } #ifdef CONFIG_USB_MUSB_OTG - put_device(musb->xceiv.dev); + put_device(musb->xceiv->dev); #endif #ifdef CONFIG_USB_MUSB_HDRC_HCD @@ -1928,10 +1942,18 @@ bad_config: } } - /* assume vbus is off */ - - /* platform adjusts musb->mregs and musb->isr if needed, - * and activates clocks + /* The musb_platform_init() call: + * - adjusts musb->mregs and musb->isr if needed, + * - may initialize an integrated tranceiver + * - initializes musb->xceiv, usually by otg_get_transceiver() + * - activates clocks. + * - stops powering VBUS + * - assigns musb->board_set_vbus if host mode is enabled + * + * There are various transciever configurations. Blackfin, + * DaVinci, TUSB60x0, and others integrate them. OMAP3 uses + * external/discrete ones in various flavors (twl4030 family, + * isp1504, non-OTG, etc) mostly hooking up through ULPI. */ musb->isr = generic_interrupt; status = musb_platform_init(musb); @@ -1968,6 +1990,10 @@ bad_config: if (status < 0) goto fail2; +#ifdef CONFIG_USB_OTG + setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); +#endif + /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); @@ -1999,17 +2025,17 @@ bad_config: ? "DMA" : "PIO", musb->nIrq); -#ifdef CONFIG_USB_MUSB_HDRC_HCD - /* host side needs more setup, except for no-host modes */ - if (musb->board_mode != MUSB_PERIPHERAL) { + /* host side needs more setup */ + if (is_host_enabled(musb)) { struct usb_hcd *hcd = musb_to_hcd(musb); - if (musb->board_mode == MUSB_OTG) + otg_set_host(musb->xceiv, &hcd->self); + + if (is_otg_enabled(musb)) hcd->self.otg_port = 1; - musb->xceiv.host = &hcd->self; + musb->xceiv->host = &hcd->self; hcd->power_budget = 2 * (plat->power ? : 250); } -#endif /* CONFIG_USB_MUSB_HDRC_HCD */ /* For the host-only role, we can activate right away. * (We expect the ID pin to be forcibly grounded!!) @@ -2017,8 +2043,8 @@ bad_config: */ if (!is_otg_enabled(musb) && is_host_enabled(musb)) { MUSB_HST_MODE(musb); - musb->xceiv.default_a = 1; - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->default_a = 1; + musb->xceiv->state = OTG_STATE_A_IDLE; status = usb_add_hcd(musb_to_hcd(musb), -1, 0); if (status) @@ -2033,8 +2059,8 @@ bad_config: } else /* peripheral is enabled */ { MUSB_DEV_MODE(musb); - musb->xceiv.default_a = 0; - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->default_a = 0; + musb->xceiv->state = OTG_STATE_B_IDLE; status = musb_gadget_setup(musb); if (status) |