X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=blobdiff_plain;f=drivers%2Fusb%2Fmusb%2Fmusb_core.c;h=ccb198c5e0fefec27c278dd20079a650c47b3cad;hp=b63ab1570103f2219afc5b44bdea08acd0531054;hb=e09ab6d3e51a01c99a0730aed51ac3d171b66184;hpb=d8f46ff11081f03b09efe82245a3257bab6bf60e diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b63ab1570103..ccb198c5e0fe 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -137,6 +137,9 @@ static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) 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); @@ -154,15 +157,22 @@ static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) 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, @@ -172,6 +182,9 @@ 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); @@ -185,15 +198,20 @@ static int musb_ulpi_write(struct otg_transceiver *otg, 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 @@ -438,8 +456,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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; } @@ -482,14 +500,20 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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", @@ -574,9 +598,10 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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: @@ -654,6 +679,15 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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) { @@ -687,7 +721,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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 @@ -916,8 +950,8 @@ void musb_start(struct musb *musb) */ 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 */ @@ -982,6 +1016,9 @@ static void musb_shutdown(struct platform_device *pdev) 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); @@ -993,6 +1030,9 @@ static void musb_shutdown(struct platform_device *pdev) musb_platform_exit(musb); pm_runtime_put(musb->controller); + + cancel_delayed_work(&musb->vbus_workaround_work); + /* FIXME power down */ } @@ -1484,15 +1524,22 @@ static irqreturn_t generic_interrupt(int irq, void *__hci) 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); @@ -1770,6 +1817,45 @@ static void musb_irq_work(struct work_struct *data) } } +#include + +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 */ @@ -1827,8 +1913,6 @@ static void musb_free(struct musb *musb) 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); @@ -1841,7 +1925,7 @@ static void musb_free(struct musb *musb) dma_controller_destroy(c); } - kfree(musb); + usb_put_hcd(musb_to_hcd(musb)); } /* @@ -1907,6 +1991,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) } if (!musb->xceiv->io_ops) { + musb->xceiv->io_dev = musb->controller; musb->xceiv->io_priv = musb->mregs; musb->xceiv->io_ops = &musb_ulpi_access; } @@ -1941,6 +2026,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* 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); @@ -1997,9 +2084,13 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) ? '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); @@ -2113,11 +2204,9 @@ static int __exit musb_remove(struct platform_device *pdev) * - 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); @@ -2158,6 +2247,7 @@ static void musb_save_context(struct musb *musb) 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 = @@ -2233,6 +2323,7 @@ static void musb_restore_context(struct musb *musb) 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, @@ -2288,21 +2379,32 @@ static int musb_suspend(struct device *dev) { 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) @@ -2374,10 +2476,7 @@ static int __init musb_init(void) 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); }