int i = 0;
u8 r;
u8 power;
+ int ret;
+
+ pm_runtime_get_sync(otg->io_dev);
/* Make sure the transceiver is not in low power mode */
power = musb_readb(addr, MUSB_POWER);
while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL)
& MUSB_ULPI_REG_CMPLT)) {
i++;
- if (i == 10000)
- return -ETIMEDOUT;
+ if (i == 10000) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
}
r = musb_readb(addr, MUSB_ULPI_REG_CONTROL);
r &= ~MUSB_ULPI_REG_CMPLT;
musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r);
- return musb_readb(addr, MUSB_ULPI_REG_DATA);
+ ret = musb_readb(addr, MUSB_ULPI_REG_DATA);
+
+out:
+ pm_runtime_put(otg->io_dev);
+
+ return ret;
}
static int musb_ulpi_write(struct otg_transceiver *otg,
int i = 0;
u8 r = 0;
u8 power;
+ int ret = 0;
+
+ pm_runtime_get_sync(otg->io_dev);
/* Make sure the transceiver is not in low power mode */
power = musb_readb(addr, MUSB_POWER);
while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL)
& MUSB_ULPI_REG_CMPLT)) {
i++;
- if (i == 10000)
- return -ETIMEDOUT;
+ if (i == 10000) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
}
r = musb_readb(addr, MUSB_ULPI_REG_CONTROL);
r &= ~MUSB_ULPI_REG_CMPLT;
musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r);
- return 0;
+out:
+ pm_runtime_put(otg->io_dev);
+
+ return ret;
}
#else
#define musb_ulpi_read NULL
if (power & MUSB_POWER_SUSPENDM) {
/* spurious */
- musb->int_usb &= ~MUSB_INTR_SUSPEND;
- dev_dbg(musb->controller, "Spurious SUSPENDM\n");
+ int_usb &= ~MUSB_INTR_SUSPEND;
+ dev_err(musb->controller, "Spurious SUSPENDM\n");
break;
}
if ((devctl & MUSB_DEVCTL_VBUS)
!= (3 << MUSB_DEVCTL_VBUS_SHIFT)
) {
- musb->int_usb |= MUSB_INTR_DISCONNECT;
- musb->int_usb &= ~MUSB_INTR_SUSPEND;
+ if (!(int_usb & MUSB_INTR_DISCONNECT))
+ dev_err(musb->controller,
+ "disconnect while suspended?\n");
+ int_usb |= MUSB_INTR_DISCONNECT;
+ int_usb &= ~MUSB_INTR_SUSPEND;
break;
}
musb_g_resume(musb);
break;
case OTG_STATE_B_IDLE:
- musb->int_usb &= ~MUSB_INTR_SUSPEND;
+ if (int_usb & MUSB_INTR_SUSPEND)
+ dev_err(musb->controller,
+ "bogus suspend+resume?\n");
+ int_usb &= ~MUSB_INTR_SUSPEND;
break;
default:
WARNING("bogus %s RESUME (%s)\n",
break;
}
- dev_dbg(musb->controller, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
+ dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller,
+ "VBUS_ERROR in %s (%02x, %02x, %s), retry #%d, port1 %08x\n",
otg_state_string(musb->xceiv->state),
- devctl,
+ devctl, power,
({ char *s;
switch (devctl & MUSB_DEVCTL_VBUS) {
case 0 << MUSB_DEVCTL_VBUS_SHIFT:
musb->is_active = 0;
break;
}
+
+ switch (musb->xceiv->state) {
+ case OTG_STATE_B_IDLE:
+ case OTG_STATE_B_PERIPHERAL:
+ cancel_delayed_work(&musb->vbus_workaround_work);
+ schedule_delayed_work(&musb->vbus_workaround_work, HZ / 2);
+ default:
+ break;
+ }
}
if (int_usb & MUSB_INTR_CONNECT) {
switch (musb->xceiv->state) {
case OTG_STATE_B_PERIPHERAL:
if (int_usb & MUSB_INTR_SUSPEND) {
- dev_dbg(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n");
+ dev_err(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n");
int_usb &= ~MUSB_INTR_SUSPEND;
goto b_host;
} else
*/
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
musb->is_active = 1;
- else
- devctl |= MUSB_DEVCTL_SESSION;
+ //else
+ // devctl |= MUSB_DEVCTL_SESSION;
} else if (is_host_enabled(musb)) {
/* assume ID pin is hard-wired to ground */
unsigned long flags;
pm_runtime_get_sync(musb->controller);
+
+ musb_gadget_cleanup(musb);
+
spin_lock_irqsave(&musb->lock, flags);
musb_platform_disable(musb);
musb_generic_disable(musb);
musb_platform_exit(musb);
pm_runtime_put(musb->controller);
+
+ cancel_delayed_work(&musb->vbus_workaround_work);
+
/* FIXME power down */
}
unsigned long flags;
irqreturn_t retval = IRQ_NONE;
struct musb *musb = __hci;
+ int i;
spin_lock_irqsave(&musb->lock, flags);
- musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
- musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
- musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+ for (i = 0; i < 8; i++) {
+ musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+ /* SOF is not enabled, but status is still often set */
+ musb->int_usb &= ~MUSB_INTR_SOF;
+ 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);
+ if (musb->int_usb || musb->int_tx || musb->int_rx)
+ retval = musb_interrupt(musb);
+ else
+ break;
+ }
spin_unlock_irqrestore(&musb->lock, flags);
}
}
+#include <linux/usb/ulpi.h>
+
+static void musb_vbus_workaround_work(struct work_struct *work)
+{
+ struct musb *musb = container_of(work, struct musb, vbus_workaround_work.work);
+ u8 devctl;
+ int ret;
+
+ if (musb_ulpi_access.write == NULL)
+ return;
+
+ pm_runtime_get_sync(musb->controller);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ /*
+ * I don't really know why but VBUS sometimes gets stuck and
+ * causes session to never end. It would look like some pullup
+ * is enabled when it shouldn't be on certain PHY states.
+ * Turning on pulldowns magically drains VBUS to zero and allows
+ * session to end, so let's do that here.
+ *
+ * XXX: probably better check VBUS on TWL?
+ * beagle sometimes has session bit set but no VBUS on twl?
+ */
+ if ((musb->xceiv->state == OTG_STATE_B_PERIPHERAL ||
+ musb->xceiv->state == OTG_STATE_B_IDLE) &&
+ (devctl & MUSB_DEVCTL_VBUS) != (3 << MUSB_DEVCTL_VBUS_SHIFT) &&
+ (devctl & MUSB_DEVCTL_VBUS) != (0 << MUSB_DEVCTL_VBUS_SHIFT)) {
+ dev_dbg(musb->controller, "VBUS workaround..\n");
+ ret = musb_ulpi_access.write(musb->xceiv, ULPI_SET(ULPI_OTG_CTRL),
+ ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN);
+ //if (ret)
+ // dev_err(musb->controller, "VBUS workaround error\n");
+ }
+
+ pm_runtime_put(musb->controller);
+}
+
/* --------------------------------------------------------------------------
* Init support
*/
sysfs_remove_group(&musb->controller->kobj, &musb_attr_group);
#endif
- musb_gadget_cleanup(musb);
-
if (musb->nIrq >= 0) {
if (musb->irq_wake)
disable_irq_wake(musb->nIrq);
dma_controller_destroy(c);
}
- kfree(musb);
+ usb_put_hcd(musb_to_hcd(musb));
}
/*
}
if (!musb->xceiv->io_ops) {
+ musb->xceiv->io_dev = musb->controller;
musb->xceiv->io_priv = musb->mregs;
musb->xceiv->io_ops = &musb_ulpi_access;
}
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
+ INIT_DELAYED_WORK(&musb->vbus_workaround_work, musb_vbus_workaround_work);
+
/* attach to the IRQ */
if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
dev_err(dev, "request_irq %d failed!\n", nIrq);
? 'B' : 'A'));
} else /* peripheral is enabled */ {
- MUSB_DEV_MODE(musb);
- musb->xceiv->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ if (musb->xceiv->default_a) {
+ MUSB_HST_MODE(musb);
+ musb->xceiv->state = OTG_STATE_A_IDLE;
+ } else {
+ MUSB_DEV_MODE(musb);
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ }
status = musb_gadget_setup(musb);
* - Peripheral mode: peripheral is deactivated (or never-activated)
* - OTG mode: both roles are deactivated (or never-activated)
*/
- pm_runtime_get_sync(musb->controller);
musb_exit_debugfs(musb);
musb_shutdown(pdev);
- pm_runtime_put(musb->controller);
musb_free(musb);
iounmap(ctrl_base);
device_init_wakeup(&pdev->dev, 0);
if (!epio)
continue;
+ musb_writeb(musb_base, MUSB_INDEX, i);
musb->context.index_regs[i].txmaxp =
musb_readw(epio, MUSB_TXMAXP);
musb->context.index_regs[i].txcsr =
if (!epio)
continue;
+ musb_writeb(musb_base, MUSB_INDEX, i);
musb_writew(epio, MUSB_TXMAXP,
musb->context.index_regs[i].txmaxp);
musb_writew(epio, MUSB_TXCSR,
{
struct musb *musb = dev_to_musb(dev);
unsigned long flags;
+ int ret = 0;
spin_lock_irqsave(&musb->lock, flags);
- if (is_peripheral_active(musb)) {
+ {
/* FIXME force disconnect unless we know USB will wake
* the system up quickly enough to respond ...
*/
- } else if (is_host_active(musb)) {
- /* we know all the children are suspended; sometimes
- * they will even be wakeup-enabled.
+ /*
+ * FIXME: musb must be already runtime suspended at this point.
+ * If it's not, framework will try to suspend it late when
+ * i2c will be off, and twl4030 will want to access it for it's
+ * stuff, causing data abort.
*/
+ int pm_usage_count =
+ atomic_read(&musb->controller->power.usage_count);
+ if (pm_usage_count > 1) {
+ dev_err(dev, "can't suspend while still active, "
+ "try removing gadget drivers (usage_count %d)\n",
+ pm_usage_count);
+ ret = -EBUSY;
+ }
}
spin_unlock_irqrestore(&musb->lock, flags);
- return 0;
+ return ret;
}
static int musb_resume_noirq(struct device *dev)
if (usb_disabled())
return 0;
- pr_info("%s: version " MUSB_VERSION ", "
- "?dma?"
- ", "
- "otg (peripheral+host)",
+ pr_info("%s: version " MUSB_VERSION ", ?dma?, otg (peripheral+host)\n",
musb_driver_name);
return platform_driver_probe(&musb_driver, musb_probe);
}