// SPDX-License-Identifier: GPL-2.0 /* * Texas Instruments DA8xx/OMAP-L1x "glue layer" * * Copyright (c) 2008-2009 MontaVista Software, Inc. <source@mvista.com> * * Based on the DaVinci "glue layer" code. * Copyright (C) 2005-2006 by Texas Instruments * * DT support * Copyright (c) 2016 Petr Kulhavy <petr@barix.com> * * This file is part of the Inventra Controller Driver for Linux.
*/
/* * Because we don't set CTRL.UINT, it's "important" to: * - not read/write INTRUSB/INTRUSBE (except during * initial setup, as a workaround); * - use INTSET/INTCLR instead.
*/
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
musb_writel(reg_base, DA8XX_USB_INTR_SRC_SET_REG,
DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT);
}
devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
} break; case OTG_STATE_A_WAIT_VFALL: /* * Wait till VBUS falls below SessionEnd (~0.2 V); the 1.3 * RTL seems to mis-handle session "start" otherwise (or in * our case "recover"), in routine "VBUS was valid by the time * VBUSERR got reported during enumeration" cases.
*/ if (devctl & MUSB_DEVCTL_VBUS) {
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); break;
}
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
musb_writel(musb->ctrl_base, DA8XX_USB_INTR_SRC_SET_REG,
MUSB_INTR_VBUSERROR << DA8XX_INTR_USB_SHIFT); break; case OTG_STATE_B_IDLE: /* * There's no ID-changed IRQ, so we have no good way to tell * when to switch to the A-Default state machine (by setting * the DEVCTL.Session bit). * * Workaround: whenever we're in B_IDLE, try setting the * session flag every few seconds. If it works, ID was * grounded and we're now in the A-Default state machine. * * NOTE: setting the session flag is _supposed_ to trigger * SRP but clearly it doesn't.
*/
musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION);
devctl = musb_readb(mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); else
musb->xceiv->otg->state = OTG_STATE_A_IDLE; break; default: break;
}
spin_unlock_irqrestore(&musb->lock, flags);
}
if (timeout == 0)
timeout = jiffies + msecs_to_jiffies(3);
/* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 &&
musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
timer_delete(&musb->dev_timer);
last_timer = jiffies; return;
}
/* * DRVVBUS IRQs are the only proxy we have (a very poor one!) for * DA8xx's missing ID change IRQ. We need an ID change IRQ to * switch appropriately between halves of the OTG state machine. * Managing DEVCTL.Session per Mentor docs requires that we know its * value but DEVCTL.BDevice is invalid without DEVCTL.Session set. * Also, DRVVBUS pulses for SRP (but not at 5 V)...
*/ if (status & (DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT)) { int drvvbus = musb_readl(reg_base, DA8XX_USB_STAT_REG); void __iomem *mregs = musb->mregs;
u8 devctl = musb_readb(mregs, MUSB_DEVCTL); int err;
err = musb->int_usb & MUSB_INTR_VBUSERROR; if (err) { /* * The Mentor core doesn't debounce VBUS as needed * to cope with device connect current spikes. This * means it's not uncommon for bus-powered devices * to get VBUS errors during enumeration. * * This is a workaround, but newer RTL from Mentor * seems to allow a better one: "re"-starting sessions * without waiting for VBUS to stop registering in * devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} elseif (drvvbus) {
MUSB_HST_MODE(musb);
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
timer_delete(&musb->dev_timer);
} elseif (!(musb->int_usb & MUSB_INTR_BABBLE)) { /* * When babble condition happens, drvvbus interrupt * is also generated. Ignore this drvvbus interrupt * and let babble interrupt handler recovers the * controller; otherwise, the host-mode flag is lost * due to the MUSB_DEV_MODE() call below and babble * recovery logic will not be called.
*/
musb->is_active = 0;
MUSB_DEV_MODE(musb);
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
if (musb->int_tx || musb->int_rx || musb->int_usb)
ret |= musb_interrupt(musb);
eoi: /* EOI needs to be written for the IRQ to be re-asserted. */ if (ret == IRQ_HANDLED || status)
musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
/* Poll for ID change */ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
ret = clk_prepare_enable(glue->clk); if (ret) {
dev_err(glue->dev, "failed to enable clock\n"); return ret;
}
/* Returns zero if e.g. not clocked */
rev = musb_readl(reg_base, DA8XX_USB_REVISION_REG); if (!rev) {
ret = -ENODEV; goto fail;
}
musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(musb->xceiv)) {
ret = -EPROBE_DEFER; goto fail;
}
timer_setup(&musb->dev_timer, otg_timer, 0);
/* Reset the controller */
musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
/* Start the on-chip PHY and its PLL. */
ret = phy_init(glue->phy); if (ret) {
dev_err(glue->dev, "Failed to init phy.\n"); goto fail;
}
ret = phy_power_on(glue->phy); if (ret) {
dev_err(glue->dev, "Failed to power on phy.\n"); goto err_phy_power_on;
}
msleep(5);
/* NOTE: IRQs are in mixed mode, not bypass to pure MUSB */
pr_debug("DA8xx OTG revision %08x, control %02x\n", rev,
musb_readb(reg_base, DA8XX_USB_CTRL_REG));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.