#include "davinci.h"
#endif
+static struct musb *the_musb;
+static struct musb_ctx ctx;
+#ifndef CONFIG_MUSB_PIO_ONLY
+static int __initdata use_dma = 1;
+#else
+static int __initdata use_dma;
+#endif
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
-unsigned musb_debug;
-module_param(musb_debug, uint, S_IRUGO | S_IWUSR);
+unsigned musb_debug = 0;
+module_param_named(debug, musb_debug, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug message level. Default = 0");
#define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia"
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" MUSB_DRIVER_NAME);
+static inline int musb_verify_charger(void __iomem *addr)
+{
+ u8 r, ret = 0;
+
+ /* Reset the transceiver */
+ r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+ r |= ISP1704_FUNC_CTRL_RESET;
+ musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+ msleep(1);
+
+ /* Set normal mode */
+ r &= ~(ISP1704_FUNC_CTRL_RESET | (3 << ISP1704_FUNC_CTRL_OPMODE));
+ musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+
+ /* Clear the DP and DM pull-down bits */
+ r = musb_ulpi_readb(addr, ISP1704_OTG_CTRL);
+ r &= ~(ISP1704_OTG_CTRL_DP_PULLDOWN | ISP1704_OTG_CTRL_DM_PULLDOWN);
+ musb_ulpi_writeb(addr, ISP1704_OTG_CTRL, r);
+
+ /* Enable strong pull-up on DP (1.5K) and reset */
+ r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+ r |= ISP1704_FUNC_CTRL_TERMSELECT | ISP1704_FUNC_CTRL_RESET;
+ musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+ msleep(1);
+
+ /* Read the line state */
+ if (musb_ulpi_readb(addr, ISP1704_DEBUG)) {
+ /* Is it a charger or PS2 connection */
+
+ /* Enable weak pull-up resistor on DP */
+ r = musb_ulpi_readb(addr, ISP1704_PWR_CTRL);
+ r |= ISP1704_PWR_CTRL_DP_WKPU_EN;
+ musb_ulpi_writeb(addr, ISP1704_PWR_CTRL, r);
+
+ /* Disable strong pull-up on DP (1.5K) */
+ r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+ r &= ~ISP1704_FUNC_CTRL_TERMSELECT;
+ musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+
+ /* Enable weak pull-down resistor on DM */
+ r = musb_ulpi_readb(addr, ISP1704_OTG_CTRL);
+ r |= ISP1704_OTG_CTRL_DM_PULLDOWN;
+ musb_ulpi_writeb(addr, ISP1704_OTG_CTRL, r);
+
+ /* It's a charger if the line states are clear */
+ if (!(musb_ulpi_readb(addr, ISP1704_DEBUG)))
+ ret = 1;
+
+ /* Disable weak pull-up resistor on DP */
+ r = musb_ulpi_readb(addr, ISP1704_PWR_CTRL);
+ r &= ~ISP1704_PWR_CTRL_DP_WKPU_EN;
+ musb_ulpi_writeb(addr, ISP1704_PWR_CTRL, r);
+ } else {
+ ret = 1;
+
+ /* Disable strong pull-up on DP (1.5K) */
+ r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+ r &= ~ISP1704_FUNC_CTRL_TERMSELECT;
+ musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+ }
+
+ return ret;
+}
+
+/* Bad connections with the charger may lead into the transceiver
+ * thinking that a device was just connected. We can wait for 5 ms to
+ * ensure that these cases will generate SUSPEND interrupt and not
+ * RESET. Reading and writing to the transceiver may still cause
+ * RESET interrupts. We mask out RESET/RESUME interrupts to
+ * recover from this.
+ */
+static int check_charger;
+static int musb_charger_detect(struct musb *musb)
+{
+ unsigned long timeout;
+
+ u8 vdat = 0;
+ u8 r;
+
+ msleep(5);
+
+ /* Using ulpi with musb is quite tricky. The following code
+ * was written based on the ulpi application note.
+ *
+ * The order of reads and writes and quite important, don't
+ * change it unless you really know what you're doing
+ */
+
+ switch(musb->xceiv->state) {
+ case OTG_STATE_B_IDLE:
+ /* we always reset transceiver */
+ check_charger = 1;
+
+ /* HACK: ULPI tends to get stuck when booting with
+ * the cable connected
+ */
+ r = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if ((r & MUSB_DEVCTL_VBUS)
+ == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
+ musb_save_ctx_and_suspend(&musb->g, 0);
+ musb_restore_ctx_and_resume(&musb->g);
+ if (musb->board && musb->board->set_pm_limits)
+ musb->board->set_pm_limits(
+ musb->controller, 1);
+ }
+
+ /* disable RESET and RESUME interrupts */
+ r = musb_readb(musb->mregs, MUSB_INTRUSBE);
+ r &= ~(MUSB_INTR_RESUME | MUSB_INTR_RESET);
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, r);
+
+ if (musb->board && musb->board->xceiv_reset)
+ musb->board->xceiv_reset();
+
+ /* then we resume to sync with controller */
+ r = musb_readb(musb->mregs, MUSB_POWER);
+ musb_writeb(musb->mregs, MUSB_POWER,
+ r | MUSB_POWER_RESUME);
+ msleep(10);
+ musb_writeb(musb->mregs, MUSB_POWER,
+ r & ~MUSB_POWER_RESUME);
+
+ /* now we set SW control bit in PWR_CTRL register */
+ musb_ulpi_writeb(musb->mregs, ISP1704_PWR_CTRL,
+ ISP1704_PWR_CTRL_SWCTRL);
+
+ r = musb_ulpi_readb(musb->mregs, ISP1704_PWR_CTRL);
+ r |= (ISP1704_PWR_CTRL_SWCTRL | ISP1704_PWR_CTRL_DPVSRC_EN);
+
+ /* and finally enable manual charger detection */
+ musb_ulpi_writeb(musb->mregs, ISP1704_PWR_CTRL, r);
+ msleep(10);
+
+ timeout = jiffies + msecs_to_jiffies(300);
+ while (!time_after(jiffies, timeout)) {
+ /* Check if there is a charger */
+ vdat = !!(musb_ulpi_readb(musb->mregs, ISP1704_PWR_CTRL)
+ & ISP1704_PWR_CTRL_VDAT_DET);
+ if (vdat)
+ break;
+ }
+ if (vdat)
+ vdat = musb_verify_charger(musb->mregs);
+
+ r &= ~ISP1704_PWR_CTRL_DPVSRC_EN;
+
+ /* Clear DPVSRC_EN, otherwise usb communication doesn't work */
+ musb_ulpi_writeb(musb->mregs, ISP1704_PWR_CTRL, r);
+ break;
+
+ default:
+ vdat = 0;
+ break;
+ }
+
+ if (vdat) {
+ /* REVISIT: This code works only with dedicated chargers!
+ * When support for HOST/HUB chargers is added, don't
+ * forget this.
+ */
+ musb_stop(musb);
+ /* Regulators off */
+ otg_set_suspend(musb->xceiv, 1);
+ musb->is_charger = 1;
+ } else {
+ /* enable interrupts */
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, ctx.intrusbe);
+
+ /* Make sure the communication starts normally */
+ r = musb_readb(musb->mregs, MUSB_POWER);
+ musb_writeb(musb->mregs, MUSB_POWER,
+ r | MUSB_POWER_RESUME);
+ msleep(10);
+ musb_writeb(musb->mregs, MUSB_POWER,
+ r & ~MUSB_POWER_RESUME);
+ }
+
+ check_charger = 0;
+
+ return vdat;
+}
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
-#ifndef CONFIG_USB_TUSB6010
+#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN)
+
/*
* Load an endpoint's FIFO
*/
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";
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_WAIT_BCON:
void __iomem *mbase = musb->mregs;
u8 reg;
- switch (musb->xceiv.state) {
+ 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->xceiv->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
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;
+ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
MUSB_DEV_MODE(musb);
reg = musb_readb(mbase, MUSB_POWER);
reg |= MUSB_POWER_SUSPENDM;
* @param power
*/
-#define STAGE0_MASK (MUSB_INTR_RESUME | MUSB_INTR_SESSREQ \
- | MUSB_INTR_VBUSERROR | MUSB_INTR_CONNECT \
- | MUSB_INTR_RESET)
-
static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
u8 devctl, u8 power)
{
irqreturn_t handled = IRQ_NONE;
void __iomem *mbase = musb->mregs;
+ u8 r;
DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
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
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;
}
#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
* - go through A_WAIT_VRISE
* - ... to A_WAIT_BCON.
* a_wait_vrise_tmout triggers VBUS_ERROR transitions
+ * NOTE : Spurious SESS_REQ int's detected, which should
+ * be discarded silently.
*/
- musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
- musb->ep0_stage = MUSB_EP0_START;
- musb->xceiv.state = OTG_STATE_A_IDLE;
- MUSB_HST_MODE(musb);
- musb_set_vbus(musb, 1);
+ if ((devctl & MUSB_DEVCTL_VBUS)
+ && !(devctl & MUSB_DEVCTL_BDEVICE)) {
+ musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
+ musb->ep0_stage = MUSB_EP0_START;
+ musb->xceiv->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ musb_set_vbus(musb, 1);
+ } else {
+ DBG(5,"discarding SESSREQ INT: VBUS < SessEnd\n");
+ }
handled = IRQ_HANDLED;
}
* 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
handled = IRQ_HANDLED;
}
+ if (int_usb & MUSB_INTR_SUSPEND) {
+ DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
+ otg_state_string(musb), devctl, power);
+ handled = IRQ_HANDLED;
+
+ 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.
+ */
+ break;
+#endif
+ case OTG_STATE_B_IDLE:
+ if (!musb->is_active)
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ musb_g_suspend(musb);
+ musb->is_active = is_otg_enabled(musb)
+ && musb->xceiv->gadget->b_hnp_enable;
+ if (musb->is_active) {
+#ifdef CONFIG_USB_MUSB_OTG
+ 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));
+#endif
+ }
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ if (musb->a_wait_bcon != 0)
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+ case OTG_STATE_A_HOST:
+ musb->xceiv->state = OTG_STATE_A_SUSPEND;
+ musb->is_active = is_otg_enabled(musb)
+ && musb->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" */
+ musb->is_active = 0;
+ break;
+ }
+ }
+
if (int_usb & MUSB_INTR_CONNECT) {
struct usb_hcd *hcd = musb_to_hcd(musb);
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;
+ musb->xceiv->state = OTG_STATE_B_HOST;
hcd->self.is_b_host = 1;
int_usb &= ~MUSB_INTR_SUSPEND;
} else
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;
+ musb->xceiv->state = OTG_STATE_B_HOST;
hcd->self.is_b_host = 1;
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;
}
#endif /* CONFIG_USB_MUSB_HDRC_HCD */
+ if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
+ DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
+ otg_state_string(musb),
+ MUSB_MODE(musb), devctl);
+ handled = IRQ_HANDLED;
+
+ switch (musb->xceiv->state) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_HOST:
+ case OTG_STATE_A_SUSPEND:
+ usb_hcd_resume_root_hub(musb_to_hcd(musb));
+ musb_root_disconnect(musb);
+ if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+#endif /* HOST */
+#ifdef CONFIG_USB_MUSB_OTG
+ case OTG_STATE_B_HOST:
+ musb_hnp_stop(musb);
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ musb_hnp_stop(musb);
+ musb_root_disconnect(musb);
+ /* FALLTHROUGH */
+ case OTG_STATE_B_WAIT_ACON:
+ /* FALLTHROUGH */
+#endif /* OTG */
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_IDLE:
+ /* Workaround for a problem of Vbus quickly dropping
+ * during Certification tests.
+ *
+ * Undo the workaround on disconnect
+ */
+
+ /* Disable suspend so we can write to ULPI */
+ r = musb_readb(musb->mregs, MUSB_POWER);
+ musb_writeb(musb->mregs, MUSB_POWER,
+ r & ~MUSB_POWER_ENSUSPEND);
+ musb_ulpi_writeb(musb->mregs,
+ ISP1704_USB_INTFALL, 0x1f);
+ musb_ulpi_writeb(musb->mregs,
+ ISP1704_USB_INTRISE, 0x1f);
+ musb_writeb(musb->mregs, MUSB_POWER,
+ r | MUSB_POWER_ENSUSPEND);
+
+ musb_g_disconnect(musb);
+ /** UGLY UGLY HACK: Windows problems with multiple
+ * configurations.
+ *
+ * This is necessary to notify gadget driver this was
+ * a physical disconnection and not only a port reset.
+ */
+ if (musb->gadget_driver->vbus_disconnect)
+ musb->gadget_driver->vbus_disconnect(&musb->g);
+
+ break;
+#endif /* GADGET */
+ default:
+ WARNING("unhandled DISCONNECT transition (%s)\n",
+ otg_state_string(musb));
+ break;
+ }
+ }
+
/* mentor saves a bit: bus reset and babble share the same irq.
* only host sees babble; only peripheral sees bus reset.
*/
if (int_usb & MUSB_INTR_RESET) {
+ handled = IRQ_HANDLED;
if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) {
/*
* Looks like non-HS BABBLE can be ignored, but
DBG(1, "BABBLE devctl: %02x\n", devctl);
else {
ERR("Stopping host session -- babble\n");
- musb_writeb(mbase, MUSB_DEVCTL, 0);
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
}
} 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
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;
+ /* Workaround the charger detection problems */
+ if ((devctl & MUSB_DEVCTL_VBUS)
+ != (3 << MUSB_DEVCTL_VBUS_SHIFT))
+ break;
+ if (check_charger)
+ break;
+ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
/* FALLTHROUGH */
case OTG_STATE_B_PERIPHERAL:
+ /* Workaround for a problem of Vbus quickly dropping
+ * during Certification tests.
+ *
+ * The guess is that vbus drops due to the circuitry
+ * for overcurrent protection and that makes transceiver
+ * think VBUS is not valid anymore. Transceiver will
+ * then send an RXCMD to PHY which will cause it to
+ * disconnect from the bus even though we disable the
+ * DISCONNECT IRQ
+ */
+ musb_ulpi_writeb(musb->mregs,
+ ISP1704_USB_INTFALL, 0x1d);
+ musb_ulpi_writeb(musb->mregs,
+ ISP1704_USB_INTRISE, 0x1d);
+
musb_g_reset(musb);
break;
default:
otg_state_string(musb));
}
}
-
- handled = IRQ_HANDLED;
}
- schedule_work(&musb->irq_work);
-
- return handled;
-}
-
-/*
- * Interrupt Service Routine to record USB "global" interrupts.
- * Since these do not happen often and signify things of
- * paramount importance, it seems OK to check them individually;
- * the order of the tests is specified in the manual
- *
- * @param musb instance pointer
- * @param int_usb register contents
- * @param devctl
- * @param power
- */
-static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
- u8 devctl, u8 power)
-{
- irqreturn_t handled = IRQ_NONE;
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
}
#endif
- if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
- DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
- otg_state_string(musb),
- MUSB_MODE(musb), devctl);
- handled = IRQ_HANDLED;
-
- switch (musb->xceiv.state) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
- case OTG_STATE_A_HOST:
- case OTG_STATE_A_SUSPEND:
- musb_root_disconnect(musb);
- if (musb->a_wait_bcon != 0)
- musb_platform_try_idle(musb, jiffies
- + msecs_to_jiffies(musb->a_wait_bcon));
- break;
-#endif /* HOST */
-#ifdef CONFIG_USB_MUSB_OTG
- case OTG_STATE_B_HOST:
- musb_hnp_stop(musb);
- break;
- case OTG_STATE_A_PERIPHERAL:
- musb_hnp_stop(musb);
- musb_root_disconnect(musb);
- /* FALLTHROUGH */
- case OTG_STATE_B_WAIT_ACON:
- /* FALLTHROUGH */
-#endif /* OTG */
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
- case OTG_STATE_B_PERIPHERAL:
- case OTG_STATE_B_IDLE:
- musb_g_disconnect(musb);
- break;
-#endif /* GADGET */
- default:
- WARNING("unhandled DISCONNECT transition (%s)\n",
- otg_state_string(musb));
- break;
- }
-
- schedule_work(&musb->irq_work);
- }
-
- if (int_usb & MUSB_INTR_SUSPEND) {
- DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
- otg_state_string(musb), devctl, power);
- handled = IRQ_HANDLED;
-
- 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.
- */
- break;
-#endif
- case OTG_STATE_B_PERIPHERAL:
- musb_g_suspend(musb);
- musb->is_active = is_otg_enabled(musb)
- && musb->xceiv.gadget->b_hnp_enable;
- if (musb->is_active) {
-#ifdef CONFIG_USB_MUSB_OTG
- 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));
-#endif
- }
- break;
- case OTG_STATE_A_WAIT_BCON:
- if (musb->a_wait_bcon != 0)
- musb_platform_try_idle(musb, jiffies
- + msecs_to_jiffies(musb->a_wait_bcon));
- break;
- case OTG_STATE_A_HOST:
- musb->xceiv.state = OTG_STATE_A_SUSPEND;
- musb->is_active = is_otg_enabled(musb)
- && musb->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" */
- musb->is_active = 0;
- break;
- }
- schedule_work(&musb->irq_work);
- }
-
+ schedule_work(&musb->irq_work);
return handled;
}
{
void __iomem *regs = musb->mregs;
u8 devctl = musb_readb(regs, MUSB_DEVCTL);
+ u8 power;
DBG(2, "<== devctl %02x\n", devctl);
+ /* Ensure the clocks are on */
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 1);
+ else
+ clk_enable(musb->clock);
+
/* Set INT enable registers, enable interrupts */
musb_writew(regs, MUSB_INTRTXE, musb->epmask);
musb_writew(regs, MUSB_INTRRXE, musb->epmask & 0xfffe);
musb_writeb(regs, MUSB_TESTMODE, 0);
+ power = MUSB_POWER_ISOUPDATE | MUSB_POWER_SOFTCONN
+ | MUSB_POWER_HSENAB;
+
+ /* ENSUSPEND wedges tusb */
+ if (musb->suspendm)
+ power |= MUSB_POWER_ENSUSPEND;
+
/* put into basic highspeed mode and start session */
- musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
- | MUSB_POWER_SOFTCONN
- | MUSB_POWER_HSENAB
- /* ENSUSPEND wedges tusb */
- /* | MUSB_POWER_ENSUSPEND */
- );
+ musb_writeb(regs, MUSB_POWER, power);
musb->is_active = 0;
devctl = musb_readb(regs, MUSB_DEVCTL);
void __iomem *mbase = musb->mregs;
u16 temp;
+ /* Clocks need to be turned on with OFF-mode */
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 1);
+ else
+ clk_enable(musb->clock);
+
/* disable interrupts */
musb_writeb(mbase, MUSB_INTRUSBE, 0);
musb_writew(mbase, MUSB_INTRTXE, 0);
}
+void musb_emergency_stop(void)
+{
+ if (!the_musb)
+ return;
+
+ musb_stop(the_musb);
+}
+EXPORT_SYMBOL_GPL(musb_emergency_stop);
+
/*
* Make the HDRC stop (disable interrupts, etc.);
* reversible by musb_start
{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 512, },
-{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 64, },
{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, },
-{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 64, },
{ .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_RX, .maxpacket = 64, },
+{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 256, },
+{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 256, },
+{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, },
+{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 256, },
+{ .hw_ep_num = 13, .style = FIFO_TX, .maxpacket = 256, },
+{ .hw_ep_num = 13, .style = FIFO_RX, .maxpacket = 4096, },
{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
};
+/* mode 5 - fits in 16KB */
+static struct fifo_cfg __initdata mode_5_cfg[] = {
+/* phonet or mass storage */
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE, },
+
+/* obex 1 */
+{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE, },
+
+/* obex 2 */
+{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE, },
+
+/* acm 1 */
+{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 16, },
+
+/* ecm */
+{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_SINGLE, },
+{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 16, },
+
+/* extras */
+{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, },
+
+{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, },
+
+{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, },
+
+{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, },
+};
/*
* configure a fifo; for non-shared endpoints, this may be called
#endif
switch (cfg->style) {
case FIFO_TX:
- musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
- musb_writew(mbase, MUSB_TXFIFOADD, c_off);
+ musb_write_txfifosz(mbase, c_size);
+ musb_write_txfifoadd(mbase, c_off);
hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_tx = maxpacket;
+ ctx.txfifosz[hw_ep->epnum] = c_size;
+ ctx.txfifoadd[hw_ep->epnum] = c_off;
break;
case FIFO_RX:
- musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
- musb_writew(mbase, MUSB_RXFIFOADD, c_off);
+ musb_write_rxfifosz(mbase, c_size);
+ musb_write_rxfifoadd(mbase, c_off);
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_rx = maxpacket;
+ ctx.rxfifosz[hw_ep->epnum] = c_size;
+ ctx.rxfifoadd[hw_ep->epnum] = c_off;
break;
case FIFO_RXTX:
- musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
- musb_writew(mbase, MUSB_TXFIFOADD, c_off);
+ musb_write_txfifosz(mbase, c_size);
+ musb_write_txfifoadd(mbase, c_off);
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_rx = maxpacket;
- musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
- musb_writew(mbase, MUSB_RXFIFOADD, c_off);
+ musb_write_rxfifosz(mbase, c_size);
+ musb_write_rxfifoadd(mbase, c_off);
hw_ep->tx_double_buffered = hw_ep->rx_double_buffered;
hw_ep->max_packet_sz_tx = maxpacket;
+ /* Save the context of endpoints. */
+ ctx.rxfifosz[hw_ep->epnum] = c_size;
+ ctx.txfifosz[hw_ep->epnum] = c_size;
+ ctx.txfifoadd[hw_ep->epnum] = c_off;
+ ctx.rxfifoadd[hw_ep->epnum] = c_off;
+
hw_ep->is_shared_fifo = true;
break;
}
int offset;
struct musb_hw_ep *hw_ep = musb->endpoints;
+ if (machine_is_nokia_rx51())
+ fifo_mode = 5;
+
switch (fifo_mode) {
default:
fifo_mode = 0;
cfg = mode_4_cfg;
n = ARRAY_SIZE(mode_4_cfg);
break;
+ case 5:
+ cfg = mode_5_cfg;
+ n = ARRAY_SIZE(mode_5_cfg);
+ break;
}
printk(KERN_DEBUG "%s: setup fifo_mode %d\n",
if (epn >= musb->config->num_eps) {
pr_debug("%s: invalid ep %d\n",
musb_driver_name, epn);
- continue;
+ return -EINVAL;
}
offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
if (offset < 0) {
*/
static int __init ep_config_from_hw(struct musb *musb)
{
- u8 epnum = 0, reg;
+ u8 epnum = 0;
struct musb_hw_ep *hw_ep;
void *mbase = musb->mregs;
+ int ret = 0;
DBG(2, "<== static silicon ep config\n");
musb_ep_select(mbase, epnum);
hw_ep = musb->endpoints + epnum;
- /* read from core using indexed model */
- reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE);
- if (!reg) {
- /* 0's returned when no more endpoints */
+ ret = musb_read_fifosize(musb, hw_ep, epnum);
+ if (ret < 0)
break;
- }
- musb->nr_endpoints++;
- musb->epmask |= (1 << epnum);
-
- hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f);
-
- /* shared TX/RX FIFO? */
- if ((reg & 0xf0) == 0xf0) {
- hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx;
- hw_ep->is_shared_fifo = true;
- continue;
- } else {
- hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4);
- hw_ep->is_shared_fifo = false;
- }
/* FIXME set up hw_ep->{rx,tx}_double_buffered */
/* log core options (read using indexed model) */
musb_ep_select(mbase, 0);
- reg = musb_readb(mbase, 0x10 + MUSB_CONFIGDATA);
+ reg = musb_read_configdata(mbase);
strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
if (reg & MUSB_CONFIGDATA_DYNFIFO)
}
/* log release info */
- hwvers = musb_readw(mbase, MUSB_HWVERS);
+ hwvers = musb_read_hwvers(mbase);
rev_major = (hwvers >> 10) & 0x1f;
rev_minor = hwvers & 0x3ff;
snprintf(aRevision, 32, "%d.%d%s", rev_major,
musb_driver_name, type, aRevision, aDate);
/* configure ep0 */
- musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
- musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
+ musb_configure_ep0(musb);
/* discover endpoint configuration */
musb->nr_endpoints = 1;
hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase;
#ifdef CONFIG_USB_MUSB_HDRC_HCD
- hw_ep->target_regs = MUSB_BUSCTL_OFFSET(i, 0) + mbase;
+ /* init list of in and out qhs */
+ INIT_LIST_HEAD(&hw_ep->in_list);
+ INIT_LIST_HEAD(&hw_ep->out_list);
+ hw_ep->target_regs = musb_read_target_reg_base(i, mbase);
hw_ep->rx_reinit = 1;
hw_ep->tx_reinit = 1;
#endif
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
- if (musb->int_usb || musb->int_tx || musb->int_rx)
- retval = musb_interrupt(musb);
+ while (musb->int_usb || musb->int_tx || musb->int_rx)
+ retval |= musb_interrupt(musb);
spin_unlock_irqrestore(&musb->lock, flags);
irqreturn_t musb_interrupt(struct musb *musb)
{
irqreturn_t retval = IRQ_NONE;
- u8 devctl, power;
+ u8 devctl, power, int_usb;
int ep_num;
u32 reg;
(devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
musb->int_usb, musb->int_tx, musb->int_rx);
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ if (is_otg_enabled(musb)|| is_peripheral_enabled(musb))
+ if (!musb->gadget_driver) {
+ DBG(5, "No gadget driver loaded\n");
+ musb->int_usb = 0;
+ musb->int_tx = 0;
+ musb->int_rx = 0;
+ return IRQ_HANDLED;
+ }
+#endif
+
/* the core can interrupt us for multiple reasons; docs have
* a generic interrupt flowchart to follow
*/
- if (musb->int_usb & STAGE0_MASK)
- retval |= musb_stage0_irq(musb, musb->int_usb,
- devctl, power);
+ int_usb = musb->int_usb;
+ musb->int_usb = 0;
+ int_usb &= ~MUSB_INTR_SOF;
+ if (int_usb)
+ retval |= musb_stage0_irq(musb, int_usb, devctl, power);
/* "stage 1" is handling endpoint irqs */
/* handle endpoint 0 first */
if (musb->int_tx & 1) {
+ musb->int_tx &= ~1;
if (devctl & MUSB_DEVCTL_HM)
retval |= musb_h_ep0_irq(musb);
else
retval |= musb_g_ep0_irq(musb);
}
- /* RX on endpoints 1-15 */
- reg = musb->int_rx >> 1;
+ /* TX on endpoints 1-15 */
+ reg = musb->int_tx >> 1;
+ musb->int_tx = 0;
ep_num = 1;
while (reg) {
if (reg & 1) {
/* musb_ep_select(musb->mregs, ep_num); */
- /* REVISIT just retval = ep->rx_irq(...) */
+ /* REVISIT just retval |= ep->tx_irq(...) */
retval = IRQ_HANDLED;
if (devctl & MUSB_DEVCTL_HM) {
if (is_host_capable())
- musb_host_rx(musb, ep_num);
+ musb_host_tx(musb, ep_num);
} else {
if (is_peripheral_capable())
- musb_g_rx(musb, ep_num);
+ musb_g_tx(musb, ep_num);
}
}
-
reg >>= 1;
ep_num++;
}
- /* TX on endpoints 1-15 */
- reg = musb->int_tx >> 1;
+ /* RX on endpoints 1-15 */
+ reg = musb->int_rx >> 1;
+ musb->int_rx = 0;
ep_num = 1;
while (reg) {
if (reg & 1) {
/* musb_ep_select(musb->mregs, ep_num); */
- /* REVISIT just retval |= ep->tx_irq(...) */
+ /* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED;
if (devctl & MUSB_DEVCTL_HM) {
if (is_host_capable())
- musb_host_tx(musb, ep_num);
+ musb_host_rx(musb, ep_num);
} else {
if (is_peripheral_capable())
- musb_g_tx(musb, ep_num);
+ musb_g_rx(musb, ep_num, false);
}
}
+
reg >>= 1;
ep_num++;
}
- /* finish handling "global" interrupts after handling fifos */
- if (musb->int_usb)
- retval |= musb_stage2_irq(musb,
- musb->int_usb, devctl, power);
-
return retval;
}
#ifndef CONFIG_MUSB_PIO_ONLY
-static int __initdata use_dma = 1;
-
-/* "modprobe ... use_dma=0" etc */
-module_param(use_dma, bool, 0);
-MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
-
void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
{
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (!epnum) {
#ifndef CONFIG_USB_TUSB_OMAP_DMA
- if (!is_cppi_enabled()) {
+ if (!cppi_ti_dma()) {
/* endpoint 0 */
if (devctl & MUSB_DEVCTL_HM)
musb_h_ep0_irq(musb);
musb_host_rx(musb, epnum);
} else {
if (is_peripheral_capable())
- musb_g_rx(musb, epnum);
+ musb_g_rx(musb, epnum, true);
}
}
}
}
-
-#else
-#define use_dma 0
#endif
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_SYSFS
static ssize_t
+musb_charger_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb *musb = dev_to_musb(dev);
+
+ return sprintf(buf, "%d\n", (musb->is_charger ?
+ musb->is_charger : musb_charger_detect(musb)));
+}
+static DEVICE_ATTR(charger, 0444, musb_charger_show, NULL);
+
+static ssize_t
+musb_amp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb *musb = dev_to_musb(dev);
+
+ return sprintf(buf, "%d\n", musb->power_draw);
+}
+static DEVICE_ATTR(mA, 0444, musb_amp_show, NULL);
+
+static ssize_t
musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct musb *musb = dev_to_musb(dev);
- unsigned long flags;
int ret = -EINVAL;
- spin_lock_irqsave(&musb->lock, flags);
+ mutex_lock(&musb->mutex);
ret = sprintf(buf, "%s\n", otg_state_string(musb));
+ mutex_unlock(&musb->mutex);
+
+ return ret;
+}
+
+static ssize_t
+musb_connect_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&musb->lock, flags);
+ ret = sprintf(buf, "%d\n", musb->softconnect);
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
}
static ssize_t
-musb_mode_store(struct device *dev, struct device_attribute *attr,
+musb_connect_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 val;
+ int status;
+ u8 power;
+
+ status = sscanf(buf, "%u", &val);
+ if (status < 1) {
+ printk(KERN_ERR "invalid parameter, %d\n", status);
+ return -EINVAL;
+ }
spin_lock_irqsave(&musb->lock, flags);
- if (!strncmp(buf, "host", 4))
- musb_platform_set_mode(musb, MUSB_HOST);
- if (!strncmp(buf, "peripheral", 10))
- musb_platform_set_mode(musb, MUSB_PERIPHERAL);
- if (!strncmp(buf, "otg", 3))
- musb_platform_set_mode(musb, MUSB_OTG);
+
+ power = musb_readb(musb->mregs, MUSB_POWER);
+
+ if (val)
+ power |= MUSB_POWER_SOFTCONN;
+ else
+ power &= ~MUSB_POWER_SOFTCONN;
+
+ musb->softconnect = !!val;
+ musb_writeb(musb->mregs, MUSB_POWER, power);
+
spin_unlock_irqrestore(&musb->lock, flags);
return n;
}
+static DEVICE_ATTR(connect, 0644, musb_connect_show, musb_connect_store);
+
+static ssize_t
+musb_mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct musb *musb = dev_to_musb(dev);
+ int status;
+
+ mutex_lock(&musb->mutex);
+ if (sysfs_streq(buf, "host"))
+ status = musb_platform_set_mode(musb, MUSB_HOST);
+ else if (sysfs_streq(buf, "peripheral"))
+ status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);
+ else if (sysfs_streq(buf, "otg"))
+ status = musb_platform_set_mode(musb, MUSB_OTG);
+ else
+ status = -EINVAL;
+ mutex_unlock(&musb->mutex);
+
+ return (status == 0) ? n : status;
+}
static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store);
static ssize_t
spin_lock_irqsave(&musb->lock, flags);
musb->a_wait_bcon = val;
- if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON)
+ 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);
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+static ssize_t
+musb_suspend_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb *musb = dev_to_musb(dev);
+
+ return sprintf(buf, "%d\n", musb->is_suspended);
+}
+static DEVICE_ATTR(suspend, 0444, musb_suspend_show, NULL);
+
/* Gadget drivers can't know that a host is connected so they might want
* to start SRP, but users can. This allows userspace to trigger SRP.
*/
static void musb_irq_work(struct work_struct *data)
{
struct musb *musb = container_of(data, struct musb, irq_work);
- static int old_state;
+ static int old_state, old_ma, old_suspend;
- 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");
}
+ if (musb->power_draw != old_ma) {
+ old_ma = musb->power_draw;
+ sysfs_notify(&musb->controller->kobj, NULL, "mA");
+ }
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ if (old_suspend != musb->is_suspended) {
+ old_suspend = musb->is_suspended;
+ sysfs_notify(&musb->controller->kobj, NULL, "suspend");
+ }
+#endif
}
/* --------------------------------------------------------------------------
#ifdef CONFIG_USB_MUSB_HDRC_HCD
struct usb_hcd *hcd;
- hcd = usb_create_hcd(&musb_hc_driver, dev, dev->bus_id);
+ hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev));
if (!hcd)
return NULL;
/* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
musb = hcd_to_musb(hcd);
- INIT_LIST_HEAD(&musb->control);
- INIT_LIST_HEAD(&musb->in_bulk);
- INIT_LIST_HEAD(&musb->out_bulk);
hcd->uses_new_polling = 1;
for (epnum = 0, ep = musb->endpoints;
epnum < musb->config->num_eps;
epnum++, ep++) {
-
ep->musb = musb;
ep->epnum = epnum;
}
*/
#ifdef CONFIG_SYSFS
+ device_remove_file(musb->controller, &dev_attr_mA);
+ device_remove_file(musb->controller, &dev_attr_connect);
+ device_remove_file(musb->controller, &dev_attr_charger);
device_remove_file(musb->controller, &dev_attr_mode);
device_remove_file(musb->controller, &dev_attr_vbus);
-#ifdef CONFIG_USB_MUSB_OTG
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ device_remove_file(musb->controller, &dev_attr_suspend);
device_remove_file(musb->controller, &dev_attr_srp);
#endif
#endif
#endif
if (musb->nIrq >= 0) {
- disable_irq_wake(musb->nIrq);
+ if (musb->irq_wake)
+ disable_irq_wake(musb->nIrq);
free_irq(musb->nIrq, musb);
}
if (is_dma_capable() && musb->dma_controller) {
}
#ifdef CONFIG_USB_MUSB_OTG
- put_device(musb->xceiv.dev);
+ put_device(musb->xceiv->dev);
#endif
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (!musb)
return -ENOMEM;
+ the_musb = musb;
+
spin_lock_init(&musb->lock);
+ mutex_init(&musb->mutex);
+ musb->board = plat->board;
musb->board_mode = plat->mode;
musb->board_set_power = plat->set_power;
musb->set_clock = plat->set_clock;
musb->min_power = plat->min_power;
+ musb->use_dma = use_dma;
/* Clock usage is chip-specific ... functional clock (DaVinci,
* OMAP2430), or PHY ref (some TUSB6010 boards). All this core
}
#endif
/* ideally this would be abstracted in platform setup */
- if (!is_dma_capable() || !musb->dma_controller)
+ if (!musb->use_dma || !musb->dma_controller)
dev->dma_mask = NULL;
/* be sure interrupts are disabled before connecting ISR */
INIT_WORK(&musb->irq_work, musb_irq_work);
/* attach to the IRQ */
- if (request_irq(nIrq, musb->isr, 0, dev->bus_id, musb)) {
+ if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
dev_err(dev, "request_irq %d failed!\n", nIrq);
status = -ENODEV;
goto fail2;
}
musb->nIrq = nIrq;
/* FIXME this handles wakeup irqs wrong */
- if (enable_irq_wake(nIrq) == 0)
+ if (enable_irq_wake(nIrq) == 0) {
+ musb->irq_wake = 1;
device_init_wakeup(dev, 1);
+ } else {
+ musb->irq_wake = 0;
+ }
pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n",
musb_driver_name,
if (musb->board_mode == MUSB_OTG)
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 */
*/
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)
} 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)
}
+ if (!(musb_debug_create("driver/musb_hdrc", musb)))
+ DBG(1, "could not create procfs entry\n");
+
#ifdef CONFIG_SYSFS
+ status = device_create_file(dev, &dev_attr_mA);
+ status = device_create_file(dev, &dev_attr_connect);
+ status = device_create_file(dev, &dev_attr_charger);
status = device_create_file(dev, &dev_attr_mode);
status = device_create_file(dev, &dev_attr_vbus);
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ status = device_create_file(dev, &dev_attr_suspend);
status = device_create_file(dev, &dev_attr_srp);
#endif /* CONFIG_USB_GADGET_MUSB_HDRC */
status = 0;
if (status)
goto fail2;
+ /* Resets the controller. Has to be done. Without this, most likely
+ * the state machine inside the transceiver doesn't get fixed properly
+ */
+ musb_save_ctx_and_suspend(&musb->g, 0);
+ musb_restore_ctx_and_resume(&musb->g);
+
return 0;
fail2:
#ifdef CONFIG_SYSFS
+ device_remove_file(dev, &dev_attr_mA);
+ device_remove_file(dev, &dev_attr_connect);
+ device_remove_file(dev, &dev_attr_charger);
device_remove_file(musb->controller, &dev_attr_mode);
device_remove_file(musb->controller, &dev_attr_vbus);
-#ifdef CONFIG_USB_MUSB_OTG
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ device_remove_file(musb->controller, &dev_attr_suspend);
device_remove_file(musb->controller, &dev_attr_srp);
#endif
#endif
/* clobbered by use_dma=n */
orig_dma_mask = dev->dma_mask;
#endif
+ /* Store initial mask for USB interrupts */
+ ctx.intrusbe = 0xf7;
+
return musb_init_controller(dev, irq, base);
}
* - OTG mode: both roles are deactivated (or never-activated)
*/
musb_shutdown(pdev);
+ musb_debug_delete("driver/musb_hdrc", musb);
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (musb->board_mode == MUSB_HOST)
usb_remove_hcd(musb_to_hcd(musb));
#ifdef CONFIG_PM
+void musb_save_ctx(struct musb *musb)
+{
+ ctx.power = musb_readb(musb->mregs, MUSB_POWER);
+ ctx.intrtxe = musb_readw(musb->mregs, MUSB_INTRTXE);
+ ctx.intrrxe = musb_readw(musb->mregs, MUSB_INTRRXE);
+ ctx.intrusbe = musb_readb(musb->mregs, MUSB_INTRUSBE);
+ ctx.devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+}
+
+void musb_restore_ctx(struct musb *musb)
+{
+ int i;
+ musb_writeb(musb->mregs, MUSB_POWER, ctx.power);
+ musb_writew(musb->mregs, MUSB_INTRTX, 0x00);
+ musb_writew(musb->mregs, MUSB_INTRTXE, ctx.intrtxe);
+ musb_writew(musb->mregs, MUSB_INTRRX, 0x00);
+ musb_writew(musb->mregs, MUSB_INTRRXE, ctx.intrrxe);
+ musb_writeb(musb->mregs, MUSB_INTRUSB, 0x00);
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, ctx.intrusbe);
+ musb_writeb(musb->mregs, MUSB_DEVCTL, ctx.devctl);
+
+ /* iterate over every endpoint, select it and restore its context */
+ for (i = 0; i < musb->config->num_eps; i++) {
+ musb_writeb(musb->mregs, MUSB_INDEX, i);
+ musb_writeb(musb->mregs, MUSB_RXFIFOSZ, ctx.rxfifosz[i]);
+ musb_writeb(musb->mregs, MUSB_TXFIFOSZ, ctx.txfifosz[i]);
+ musb_writew(musb->mregs, MUSB_TXFIFOADD, ctx.txfifoadd[i]);
+ musb_writew(musb->mregs, MUSB_RXFIFOADD, ctx.rxfifoadd[i]);
+ };
+}
+
static int musb_suspend(struct platform_device *pdev, pm_message_t message)
{
unsigned long flags;
*/
}
+ /* save context */
+ musb_save_ctx(musb);
+
if (musb->set_clock)
musb->set_clock(musb->clock, 0);
else
clk_disable(musb->clock);
+
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
}
else
clk_enable(musb->clock);
+ /* restore context */
+ musb_restore_ctx(musb);
+
/* for static cmos like DaVinci, register values were preserved
* unless for some reason the whole soc powered down and we're
* not treating that as a whole-system restart (e.g. swsusp)