diff options
Diffstat (limited to 'usb/usb-musb-introduce-da8xx-omap-l1x-glue-layer.patch')
| -rw-r--r-- | usb/usb-musb-introduce-da8xx-omap-l1x-glue-layer.patch | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/usb/usb-musb-introduce-da8xx-omap-l1x-glue-layer.patch b/usb/usb-musb-introduce-da8xx-omap-l1x-glue-layer.patch new file mode 100644 index 00000000000000..2d331bb0723f70 --- /dev/null +++ b/usb/usb-musb-introduce-da8xx-omap-l1x-glue-layer.patch @@ -0,0 +1,544 @@ +From linux-usb-owner@vger.kernel.org Tue Oct 5 13:28:43 2010 +From: Felipe Balbi <balbi@ti.com> +To: Greg KH <greg@kroah.com> +Cc: Linux USB Mailing List <linux-usb@vger.kernel.org>, + Sergei Shtylyov <sshtylyov@ru.mvista.com>, + Yadviga Grigorieva <yadviga@ru.mvista.com>, + Felipe Balbi <balbi@ti.com> +Subject: usb: musb: introduce DA8xx/OMAP-L1x glue layer +Date: Fri, 24 Sep 2010 13:44:03 +0300 +Message-Id: <1285325055-1247-3-git-send-email-balbi@ti.com> + +From: Sergei Shtylyov <sshtylyov@ru.mvista.com> + +Texas Instruments DA8xx/OMAP-L1x glue layer for the +MUSBMHRDC driver. + +Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> +Signed-off-by: Yadviga Grigorieva <yadviga@ru.mvista.com> +Signed-off-by: Felipe Balbi <balbi@ti.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/musb/Kconfig | 5 + drivers/usb/musb/Makefile | 4 + drivers/usb/musb/da8xx.c | 469 +++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/musb/musb_core.h | 1 + 4 files changed, 478 insertions(+), 1 deletion(-) + +--- a/drivers/usb/musb/Kconfig ++++ b/drivers/usb/musb/Kconfig +@@ -45,6 +45,9 @@ config USB_MUSB_SOC + comment "DaVinci 35x and 644x USB support" + depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx + ++comment "DA8xx/OMAP-L1x USB support" ++ depends on USB_MUSB_HDRC && ARCH_DAVINCI_DA8XX ++ + comment "OMAP 243x high speed USB support" + depends on USB_MUSB_HDRC && ARCH_OMAP2430 + +@@ -144,7 +147,7 @@ config USB_MUSB_HDRC_HCD + config MUSB_PIO_ONLY + bool 'Disable DMA (always use PIO)' + depends on USB_MUSB_HDRC +- default y if USB_TUSB6010 ++ default USB_TUSB6010 || ARCH_DAVINCI_DA8XX + help + All data is copied between memory and FIFO by the CPU. + DMA controllers are ignored. +--- a/drivers/usb/musb/Makefile ++++ b/drivers/usb/musb/Makefile +@@ -10,6 +10,10 @@ ifeq ($(CONFIG_ARCH_DAVINCI_DMx),y) + musb_hdrc-objs += davinci.o + endif + ++ifeq ($(CONFIG_ARCH_DAVINCI_DA8XX),y) ++ musb_hdrc-objs += da8xx.o ++endif ++ + ifeq ($(CONFIG_USB_TUSB6010),y) + musb_hdrc-objs += tusb6010.o + endif +--- /dev/null ++++ b/drivers/usb/musb/da8xx.c +@@ -0,0 +1,469 @@ ++/* ++ * 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 ++ * ++ * This file is part of the Inventra Controller Driver for Linux. ++ * ++ * The Inventra Controller Driver for Linux is free software; you ++ * can redistribute it and/or modify it under the terms of the GNU ++ * General Public License version 2 as published by the Free Software ++ * Foundation. ++ * ++ * The Inventra Controller Driver for Linux is distributed in ++ * the hope that it will be useful, but WITHOUT ANY WARRANTY; ++ * without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public ++ * License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with The Inventra Controller Driver for Linux ; if not, ++ * write to the Free Software Foundation, Inc., 59 Temple Place, ++ * Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include <linux/init.h> ++#include <linux/clk.h> ++#include <linux/io.h> ++ ++#include <mach/da8xx.h> ++#include <mach/usb.h> ++ ++#include "musb_core.h" ++ ++/* ++ * DA8XX specific definitions ++ */ ++ ++/* USB 2.0 OTG module registers */ ++#define DA8XX_USB_REVISION_REG 0x00 ++#define DA8XX_USB_CTRL_REG 0x04 ++#define DA8XX_USB_STAT_REG 0x08 ++#define DA8XX_USB_EMULATION_REG 0x0c ++#define DA8XX_USB_MODE_REG 0x10 /* Transparent, CDC, [Generic] RNDIS */ ++#define DA8XX_USB_AUTOREQ_REG 0x14 ++#define DA8XX_USB_SRP_FIX_TIME_REG 0x18 ++#define DA8XX_USB_TEARDOWN_REG 0x1c ++#define DA8XX_USB_INTR_SRC_REG 0x20 ++#define DA8XX_USB_INTR_SRC_SET_REG 0x24 ++#define DA8XX_USB_INTR_SRC_CLEAR_REG 0x28 ++#define DA8XX_USB_INTR_MASK_REG 0x2c ++#define DA8XX_USB_INTR_MASK_SET_REG 0x30 ++#define DA8XX_USB_INTR_MASK_CLEAR_REG 0x34 ++#define DA8XX_USB_INTR_SRC_MASKED_REG 0x38 ++#define DA8XX_USB_END_OF_INTR_REG 0x3c ++#define DA8XX_USB_GENERIC_RNDIS_EP_SIZE_REG(n) (0x50 + (((n) - 1) << 2)) ++ ++/* Control register bits */ ++#define DA8XX_SOFT_RESET_MASK 1 ++ ++#define DA8XX_USB_TX_EP_MASK 0x1f /* EP0 + 4 Tx EPs */ ++#define DA8XX_USB_RX_EP_MASK 0x1e /* 4 Rx EPs */ ++ ++/* USB interrupt register bits */ ++#define DA8XX_INTR_USB_SHIFT 16 ++#define DA8XX_INTR_USB_MASK (0x1ff << DA8XX_INTR_USB_SHIFT) /* 8 Mentor */ ++ /* interrupts and DRVVBUS interrupt */ ++#define DA8XX_INTR_DRVVBUS 0x100 ++#define DA8XX_INTR_RX_SHIFT 8 ++#define DA8XX_INTR_RX_MASK (DA8XX_USB_RX_EP_MASK << DA8XX_INTR_RX_SHIFT) ++#define DA8XX_INTR_TX_SHIFT 0 ++#define DA8XX_INTR_TX_MASK (DA8XX_USB_TX_EP_MASK << DA8XX_INTR_TX_SHIFT) ++ ++#define DA8XX_MENTOR_CORE_OFFSET 0x400 ++ ++#define CFGCHIP2 IO_ADDRESS(DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP2_REG) ++ ++/* ++ * REVISIT (PM): we should be able to keep the PHY in low power mode most ++ * of the time (24 MHz oscillator and PLL off, etc.) by setting POWER.D0 ++ * and, when in host mode, autosuspending idle root ports... PHY_PLLON ++ * (overriding SUSPENDM?) then likely needs to stay off. ++ */ ++ ++static inline void phy_on(void) ++{ ++ u32 cfgchip2 = __raw_readl(CFGCHIP2); ++ ++ /* ++ * Start the on-chip PHY and its PLL. ++ */ ++ cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN); ++ cfgchip2 |= CFGCHIP2_PHY_PLLON; ++ __raw_writel(cfgchip2, CFGCHIP2); ++ ++ pr_info("Waiting for USB PHY clock good...\n"); ++ while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD)) ++ cpu_relax(); ++} ++ ++static inline void phy_off(void) ++{ ++ u32 cfgchip2 = __raw_readl(CFGCHIP2); ++ ++ /* ++ * Ensure that USB 1.1 reference clock is not being sourced from ++ * USB 2.0 PHY. Otherwise do not power down the PHY. ++ */ ++ if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX) && ++ (cfgchip2 & CFGCHIP2_USB1SUSPENDM)) { ++ pr_warning("USB 1.1 clocked from USB 2.0 PHY -- " ++ "can't power it down\n"); ++ return; ++ } ++ ++ /* ++ * Power down the on-chip PHY. ++ */ ++ cfgchip2 |= CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN; ++ __raw_writel(cfgchip2, CFGCHIP2); ++} ++ ++/* ++ * 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. ++ */ ++ ++/** ++ * musb_platform_enable - enable interrupts ++ */ ++void musb_platform_enable(struct musb *musb) ++{ ++ void __iomem *reg_base = musb->ctrl_base; ++ u32 mask; ++ ++ /* Workaround: setup IRQs through both register sets. */ ++ mask = ((musb->epmask & DA8XX_USB_TX_EP_MASK) << DA8XX_INTR_TX_SHIFT) | ++ ((musb->epmask & DA8XX_USB_RX_EP_MASK) << DA8XX_INTR_RX_SHIFT) | ++ DA8XX_INTR_USB_MASK; ++ musb_writel(reg_base, DA8XX_USB_INTR_MASK_SET_REG, mask); ++ ++ /* Force the DRVVBUS IRQ so we can start polling for ID change. */ ++ if (is_otg_enabled(musb)) ++ musb_writel(reg_base, DA8XX_USB_INTR_SRC_SET_REG, ++ DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT); ++} ++ ++/** ++ * musb_platform_disable - disable HDRC and flush interrupts ++ */ ++void musb_platform_disable(struct musb *musb) ++{ ++ void __iomem *reg_base = musb->ctrl_base; ++ ++ musb_writel(reg_base, DA8XX_USB_INTR_MASK_CLEAR_REG, ++ DA8XX_INTR_USB_MASK | ++ DA8XX_INTR_TX_MASK | DA8XX_INTR_RX_MASK); ++ musb_writeb(musb->mregs, MUSB_DEVCTL, 0); ++ musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0); ++} ++ ++#ifdef CONFIG_USB_MUSB_HDRC_HCD ++#define portstate(stmt) stmt ++#else ++#define portstate(stmt) ++#endif ++ ++static void da8xx_set_vbus(struct musb *musb, int is_on) ++{ ++ WARN_ON(is_on && is_peripheral_active(musb)); ++} ++ ++#define POLL_SECONDS 2 ++ ++static struct timer_list otg_workaround; ++ ++static void otg_timer(unsigned long _musb) ++{ ++ struct musb *musb = (void *)_musb; ++ void __iomem *mregs = musb->mregs; ++ u8 devctl; ++ unsigned long flags; ++ ++ /* ++ * We poll because DaVinci's won't expose several OTG-critical ++ * status change events (from the transceiver) otherwise. ++ */ ++ devctl = musb_readb(mregs, MUSB_DEVCTL); ++ DBG(7, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); ++ ++ spin_lock_irqsave(&musb->lock, flags); ++ switch (musb->xceiv->state) { ++ case OTG_STATE_A_WAIT_BCON: ++ devctl &= ~MUSB_DEVCTL_SESSION; ++ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); ++ ++ devctl = musb_readb(musb->mregs, MUSB_DEVCTL); ++ if (devctl & MUSB_DEVCTL_BDEVICE) { ++ musb->xceiv->state = OTG_STATE_B_IDLE; ++ MUSB_DEV_MODE(musb); ++ } else { ++ musb->xceiv->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(&otg_workaround, jiffies + POLL_SECONDS * HZ); ++ break; ++ } ++ musb->xceiv->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: ++ if (!is_peripheral_enabled(musb)) ++ break; ++ ++ /* ++ * 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(&otg_workaround, jiffies + POLL_SECONDS * HZ); ++ else ++ musb->xceiv->state = OTG_STATE_A_IDLE; ++ break; ++ default: ++ break; ++ } ++ spin_unlock_irqrestore(&musb->lock, flags); ++} ++ ++void musb_platform_try_idle(struct musb *musb, unsigned long timeout) ++{ ++ static unsigned long last_timer; ++ ++ if (!is_otg_enabled(musb)) ++ return; ++ ++ 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->state == OTG_STATE_A_WAIT_BCON)) { ++ DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); ++ del_timer(&otg_workaround); ++ last_timer = jiffies; ++ return; ++ } ++ ++ if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { ++ DBG(4, "Longer idle timer already pending, ignoring...\n"); ++ return; ++ } ++ last_timer = timeout; ++ ++ DBG(4, "%s inactive, starting idle timer for %u ms\n", ++ otg_state_string(musb), jiffies_to_msecs(timeout - jiffies)); ++ mod_timer(&otg_workaround, timeout); ++} ++ ++static irqreturn_t da8xx_interrupt(int irq, void *hci) ++{ ++ struct musb *musb = hci; ++ void __iomem *reg_base = musb->ctrl_base; ++ unsigned long flags; ++ irqreturn_t ret = IRQ_NONE; ++ u32 status; ++ ++ spin_lock_irqsave(&musb->lock, flags); ++ ++ /* ++ * NOTE: DA8XX shadows the Mentor IRQs. Don't manage them through ++ * the Mentor registers (except for setup), use the TI ones and EOI. ++ */ ++ ++ /* Acknowledge and handle non-CPPI interrupts */ ++ status = musb_readl(reg_base, DA8XX_USB_INTR_SRC_MASKED_REG); ++ if (!status) ++ goto eoi; ++ ++ musb_writel(reg_base, DA8XX_USB_INTR_SRC_CLEAR_REG, status); ++ DBG(4, "USB IRQ %08x\n", status); ++ ++ musb->int_rx = (status & DA8XX_INTR_RX_MASK) >> DA8XX_INTR_RX_SHIFT; ++ musb->int_tx = (status & DA8XX_INTR_TX_MASK) >> DA8XX_INTR_TX_SHIFT; ++ musb->int_usb = (status & DA8XX_INTR_USB_MASK) >> DA8XX_INTR_USB_SHIFT; ++ ++ /* ++ * 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 = is_host_enabled(musb) && (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->state = OTG_STATE_A_WAIT_VFALL; ++ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); ++ WARNING("VBUS error workaround (delay coming)\n"); ++ } else if (is_host_enabled(musb) && drvvbus) { ++ MUSB_HST_MODE(musb); ++ musb->xceiv->default_a = 1; ++ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; ++ portstate(musb->port1_status |= USB_PORT_STAT_POWER); ++ del_timer(&otg_workaround); ++ } else { ++ musb->is_active = 0; ++ MUSB_DEV_MODE(musb); ++ musb->xceiv->default_a = 0; ++ musb->xceiv->state = OTG_STATE_B_IDLE; ++ portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); ++ } ++ ++ DBG(2, "VBUS %s (%s)%s, devctl %02x\n", ++ drvvbus ? "on" : "off", ++ otg_state_string(musb), ++ err ? " ERROR" : "", ++ devctl); ++ ret = IRQ_HANDLED; ++ } ++ ++ 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 (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE) ++ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); ++ ++ spin_unlock_irqrestore(&musb->lock, flags); ++ ++ return ret; ++} ++ ++int musb_platform_set_mode(struct musb *musb, u8 musb_mode) ++{ ++ u32 cfgchip2 = __raw_readl(CFGCHIP2); ++ ++ cfgchip2 &= ~CFGCHIP2_OTGMODE; ++ switch (musb_mode) { ++#ifdef CONFIG_USB_MUSB_HDRC_HCD ++ case MUSB_HOST: /* Force VBUS valid, ID = 0 */ ++ cfgchip2 |= CFGCHIP2_FORCE_HOST; ++ break; ++#endif ++#ifdef CONFIG_USB_GADGET_MUSB_HDRC ++ case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */ ++ cfgchip2 |= CFGCHIP2_FORCE_DEVICE; ++ break; ++#endif ++#ifdef CONFIG_USB_MUSB_OTG ++ case MUSB_OTG: /* Don't override the VBUS/ID comparators */ ++ cfgchip2 |= CFGCHIP2_NO_OVERRIDE; ++ break; ++#endif ++ default: ++ DBG(2, "Trying to set unsupported mode %u\n", musb_mode); ++ } ++ ++ __raw_writel(cfgchip2, CFGCHIP2); ++ return 0; ++} ++ ++int __init musb_platform_init(struct musb *musb, void *board_data) ++{ ++ void __iomem *reg_base = musb->ctrl_base; ++ u32 rev; ++ ++ musb->mregs += DA8XX_MENTOR_CORE_OFFSET; ++ ++ clk_enable(musb->clock); ++ ++ /* Returns zero if e.g. not clocked */ ++ rev = musb_readl(reg_base, DA8XX_USB_REVISION_REG); ++ if (!rev) ++ goto fail; ++ ++ usb_nop_xceiv_register(); ++ musb->xceiv = otg_get_transceiver(); ++ if (!musb->xceiv) ++ goto fail; ++ ++ if (is_host_enabled(musb)) ++ setup_timer(&otg_workaround, otg_timer, (unsigned long)musb); ++ ++ musb->board_set_vbus = da8xx_set_vbus; ++ ++ /* Reset the controller */ ++ musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK); ++ ++ /* Start the on-chip PHY and its PLL. */ ++ phy_on(); ++ ++ msleep(5); ++ ++ /* NOTE: IRQs are in mixed mode, not bypass to pure MUSB */ ++ pr_debug("DA8xx OTG revision %08x, PHY %03x, control %02x\n", ++ rev, __raw_readl(CFGCHIP2), ++ musb_readb(reg_base, DA8XX_USB_CTRL_REG)); ++ ++ musb->isr = da8xx_interrupt; ++ return 0; ++fail: ++ clk_disable(musb->clock); ++ return -ENODEV; ++} ++ ++int musb_platform_exit(struct musb *musb) ++{ ++ if (is_host_enabled(musb)) ++ del_timer_sync(&otg_workaround); ++ ++ phy_off(); ++ ++ otg_put_transceiver(musb->xceiv); ++ usb_nop_xceiv_unregister(); ++ ++ clk_disable(musb->clock); ++ ++ return 0; ++} +--- a/drivers/usb/musb/musb_core.h ++++ b/drivers/usb/musb/musb_core.h +@@ -599,6 +599,7 @@ extern void musb_hnp_stop(struct musb *m + extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode); + + #if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \ ++ defined(CONFIG_ARCH_DAVINCI_DA8XX) || \ + defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \ + defined(CONFIG_ARCH_OMAP4) + extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout); |
