// SPDX-License-Identifier: GPL-2.0 /* * TUSB6010 USB 2.0 OTG Dual Role controller * * Copyright (C) 2006 Nokia Corporation * Tony Lindgren <tony@atomide.com> * * Notes: * - Driver assumes that interface to external host (main CPU) is * configured for NOR FLASH interface instead of VLYNQ serial * interface.
*/
/* * Checks the revision. We need to use the DMA register as 3.0 does not * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV.
*/ static u8 tusb_get_revision(struct musb *musb)
{ void __iomem *tbase = musb->ctrl_base;
u32 die_id;
u8 rev;
if (epnum)
musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
TUSB_EP_CONFIG_XFR_SIZE(len)); else
musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_XFR_SIZE(len));
if (likely((0x01 & (unsignedlong) buf) == 0)) {
/* Best case is 32bit-aligned destination address */ if ((0x02 & (unsignedlong) buf) == 0) { if (len >= 4) {
ioread32_rep(fifo, buf, len >> 2);
buf += (len & ~0x03);
len &= 0x03;
}
} else { if (len >= 2) {
u32 val; int i;
/* Cannot use readsw, fifo is 32-bit */ for (i = 0; i < (len >> 2); i++) {
val = musb_readl(fifo, 0);
*(u16 *)buf = (u16)(val & 0xffff);
buf += 2;
*(u16 *)buf = (u16)(val >> 16);
buf += 2;
}
len &= 0x03;
}
}
}
if (len > 0)
tusb_fifo_read_unaligned(fifo, buf, len);
}
staticstruct musb *the_musb;
/* This is used by gadget drivers, and OTG transceiver logic, allowing * at most mA current to be drawn from VBUS during a Default-B session * (that is, while VBUS exceeds 4.4V). In Default-A (including pure host * mode), or low power Default-B sessions, something else supplies power. * Caller must take care of locking.
*/ staticint tusb_draw_power(struct usb_phy *x, unsigned mA)
{ struct musb *musb = the_musb; void __iomem *tbase = musb->ctrl_base;
u32 reg;
/* tps65030 seems to consume max 100mA, with maybe 60mA available * (measured on one board) for things other than tps and tusb. * * Boards sharing the CPU clock with CLKIN will need to prevent * certain idle sleep states while the USB link is active. * * REVISIT we could use VBUS to supply only _one_ of { 1.5V, 3.3V }. * The actual current usage would be very board-specific. For now, * it's simpler to just use an aggregate (also board-specific).
*/ if (x->otg->default_a || mA < (musb->min_power << 1))
mA = 0;
/* * Idle TUSB6010 until next wake-up event; NOR access always wakes. * Other code ensures that we idle unless we're connected _and_ the * USB link is not suspended ... and tells us the relevant wakeup * events. SW_EN for voltage is handled separately.
*/ staticvoid tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
{ void __iomem *tbase = musb->ctrl_base;
u32 reg;
if ((wakeup_enables & TUSB_PRCM_WBUS)
&& (musb->tusb_revision == TUSB_REV_30))
tusb_wbus_quirk(musb, 1);
/* REVISIT writeup of WID implies that if WID set and ID is grounded, * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared. * Presumably that's mostly to save power, hence WID is immaterial ...
*/
dev_dbg(musb->controller, "idle, wake on %02x\n", wakeup_enables);
}
/* * Updates cable VBUS status. Caller must take care of locking.
*/ staticint tusb_musb_vbus_status(struct musb *musb)
{ void __iomem *tbase = musb->ctrl_base;
u32 otg_stat, prcm_mngmt; int ret = 0;
/* * Maybe put TUSB6010 into idle mode depending on USB link status, * like "disconnected" or "suspended". We'll be woken out of it by * connect, resume, or disconnect. * * Needs to be called as the last function everywhere where there is * register access to TUSB6010 because of NOR flash wake-up. * Caller should own controller spinlock. * * Delay because peripheral enables D+ pullup 3msec after SE0, and * we don't want to treat that full speed J as a wakeup event. * ... peripherals must draw only suspend current after 10 msec.
*/ staticvoid tusb_musb_try_idle(struct musb *musb, unsignedlong timeout)
{ unsignedlong default_timeout = jiffies + msecs_to_jiffies(3); staticunsignedlong last_timer;
if (timeout == 0)
timeout = default_timeout;
/* 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;
}
/* * Sets the mode to OTG, peripheral or host by changing the ID detection. * Caller must take care of locking. * * Note that if a mini-A cable is plugged in the ID line will stay down as * the weak ID pull-up is not able to pull the ID up.
*/ staticint tusb_musb_set_mode(struct musb *musb, u8 musb_mode)
{ void __iomem *tbase = musb->ctrl_base;
u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf;
switch (musb->xceiv->otg->state) { case OTG_STATE_A_IDLE:
dev_dbg(musb->controller, "Got SRP, turning on VBUS\n");
musb_platform_set_vbus(musb, 1);
/* CONNECT can wake if a_wait_bcon is set */ if (musb->a_wait_bcon != 0)
musb->is_active = 0; else
musb->is_active = 1;
/* * OPT FS A TD.4.6 needs few seconds for * A_WAIT_VRISE
*/
idle_timeout = jiffies + (2 * HZ);
break; case OTG_STATE_A_WAIT_VRISE: /* ignore; A-session-valid < VBUS_VALID/2, * we monitor this with the timer
*/ break; case OTG_STATE_A_WAIT_VFALL: /* REVISIT this irq triggers during short * spikes caused by enumeration ...
*/ if (musb->vbuserr_retry) {
musb->vbuserr_retry--;
tusb_musb_set_vbus(musb, 1);
} else {
musb->vbuserr_retry
= VBUSERR_RETRY_COUNT;
tusb_musb_set_vbus(musb, 0);
} break; default: break;
}
}
}
switch (musb->xceiv->otg->state) { case OTG_STATE_A_WAIT_VRISE: /* VBUS has probably been valid for a while now, * but may well have bounced out of range a bit
*/
devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) { if ((devctl & MUSB_DEVCTL_VBUS)
!= MUSB_DEVCTL_VBUS) {
dev_dbg(musb->controller, "devctl %02x\n", devctl); break;
}
musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
musb->is_active = 0;
idle_timeout = jiffies
+ msecs_to_jiffies(musb->a_wait_bcon);
} else { /* REVISIT report overcurrent to hub? */
ERR("vbus too slow, devctl %02x\n", devctl);
tusb_musb_set_vbus(musb, 0);
} break; case OTG_STATE_A_WAIT_BCON: if (musb->a_wait_bcon != 0)
idle_timeout = jiffies
+ msecs_to_jiffies(musb->a_wait_bcon); break; case OTG_STATE_A_SUSPEND: break; case OTG_STATE_B_WAIT_ACON: break; default: break;
}
}
schedule_delayed_work(&musb->irq_work, 0);
/* Mask all interrupts to allow using both edge and level GPIO irq */
int_mask = musb_readl(tbase, TUSB_INT_MASK);
musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS);
if (musb->tusb_revision == TUSB_REV_30)
tusb_wbus_quirk(musb, 0);
/* there are issues re-locking the PLL on wakeup ... */
/* work around issue 8 */ for (i = 0xf7f7f7; i > 0xf7f7f7 - 1000; i--) {
musb_writel(tbase, TUSB_SCRATCH_PAD, 0);
musb_writel(tbase, TUSB_SCRATCH_PAD, i);
reg = musb_readl(tbase, TUSB_SCRATCH_PAD); if (reg == i) break;
dev_dbg(musb->controller, "TUSB NOR not ready\n");
}
/* work around issue 13 (2nd half) */
tusb_set_clock_source(musb, 1);
/* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */
}
if (int_src & TUSB_INT_SRC_USB_IP_CONN)
timer_delete(&musb->dev_timer);
/* OTG state change reports (annoyingly) not issued by Mentor core */ if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG
| TUSB_INT_SRC_OTG_TIMEOUT
| TUSB_INT_SRC_ID_STATUS_CHNG))
idle_timeout = tusb_otg_ints(musb, int_src, tbase);
/* * Just clear the DMA interrupt if it comes as the completion for both * TX and RX is handled by the DMA callback in tusb6010_omap
*/ if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) {
u32 dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC);
/* * Enables TUSB6010. Caller must take care of locking. * REVISIT: * - Check what is unnecessary in MGC_HdrcStart()
*/ staticvoid tusb_musb_enable(struct musb *musb)
{ void __iomem *tbase = musb->ctrl_base;
/* Setup TUSB6010 main interrupt mask. Enable all interrupts except SOF.
* REVISIT: Enable and deal with TUSB_INT_SRC_USB_IP_SOF */
musb_writel(tbase, TUSB_INT_MASK, TUSB_INT_SRC_USB_IP_SOF);
/* Only 0 clock cycles for minimum interrupt de-assertion time and
* interrupt polarity active low seems to work reliably here */
musb_writel(tbase, TUSB_INT_CTRL_CONF,
TUSB_INT_CTRL_CONF_INT_RELCYC(0));
irq_set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW);
/* maybe force into the Default-A OTG state machine */ if (!(musb_readl(tbase, TUSB_DEV_OTG_STAT)
& TUSB_DEV_OTG_STAT_ID_STATUS))
musb_writel(tbase, TUSB_INT_SRC_SET,
TUSB_INT_SRC_ID_STATUS_CHNG);
if (is_dma_capable() && dma_off)
printk(KERN_WARNING "%s %s: dma not reactivated\n",
__FILE__, __func__); else
dma_off = 1;
}
/* * Disables TUSB6010. Caller must take care of locking.
*/ staticvoid tusb_musb_disable(struct musb *musb)
{ void __iomem *tbase = musb->ctrl_base;
if (is_dma_capable() && !dma_off) {
printk(KERN_WARNING "%s %s: dma still active\n",
__FILE__, __func__);
dma_off = 1;
}
}
/* * Sets up TUSB6010 CPU interface specific signals and registers * Note: Settings optimized for OMAP24xx
*/ staticvoid tusb_setup_cpu_interface(struct musb *musb)
{ void __iomem *tbase = musb->ctrl_base;
/* * Disable GPIO[5:0] pullups (used as output DMA requests) * Don't disable GPIO[7:6] as they are needed for wake-up.
*/
musb_writel(tbase, TUSB_PULLUP_1_CTRL, 0x0000003F);
/* Disable all pullups on NOR IF, DMAREQ0 and DMAREQ1 */
musb_writel(tbase, TUSB_PULLUP_2_CTRL, 0x01FFFFFF);
/* Turn GPIO[5:0] to DMAREQ[5:0] signals */
musb_writel(tbase, TUSB_GPIO_CONF, TUSB_GPIO_CONF_DMAREQ(0x3f));
/* Burst size 16x16 bits, all six DMA requests enabled, DMA request
* de-assertion time 2 system clocks p 62 */
musb_writel(tbase, TUSB_DMA_REQ_CONF,
TUSB_DMA_REQ_CONF_BURST_SIZE(2) |
TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) |
TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2));
/* Set 0 wait count for synchronous burst access */
musb_writel(tbase, TUSB_WAIT_COUNT, 1);
}
/* * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and * 1.5 V voltage regulators of PM companion chip. Companion chip will then * provide then PGOOD signal to TUSB6010 which will release it from reset.
*/
gpiod_set_value(glue->enable, 1);
/* Wait for 100ms until TUSB6010 pulls INT pin down */
ret = read_poll_timeout(gpiod_get_value, reg, !reg, 5000, 100000, true,
glue->intpin); if (ret) {
pr_err("tusb: Powerup response failed\n"); return ret;
}
spin_lock_irqsave(&musb->lock, flags);
if (musb_readl(tbase, TUSB_PROD_TEST_RESET) !=
TUSB_PROD_TEST_RESET_VAL) {
printk(KERN_ERR "tusb: Unable to detect TUSB6010\n"); goto err;
}
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.