#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/memory.h>
-// #include <asm/arch/gpio.h>
+#include <asm/arch/gpio.h>
#include <asm/mach-types.h>
#include "musbdefs.h"
-#ifdef CONFIG_ARCH_DAVINCI
-
#ifdef CONFIG_MACH_DAVINCI_EVM
#include <asm/arch/i2c-client.h>
#endif
#include "davinci.h"
-#endif
-
-#ifdef CONFIG_USB_TI_CPPI_DMA
#include "cppi_dma.h"
-#endif
+/* 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... PHYPLLON
+ * (overriding SUSPENDM?) then likely needs to stay off.
+ */
+
static inline void phy_on(void)
{
/* start the on-chip PHY and its PLL */
static inline void phy_off(void)
{
/* powerdown the on-chip PHY and its oscillator */
- __raw_writel(USBPHY_OSCPDWN | USBPHY_PHYSPDWN,
+ __raw_writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN,
IO_ADDRESS(USBPHY_CTL_PADDR));
}
static void session(struct musb *musb, int is_on)
{
void *__iomem mregs = musb->pRegs;
- u8 devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
- /* NOTE: after drvvbus off the state _could_ be A_IDLE;
- * but the silicon seems to couple vbus to "ID grounded".
- */
- devctl |= MGC_M_DEVCTL_SESSION;
+ if (musb->xceiv.default_a) {
+ u8 devctl = musb_readb(mregs, MGC_O_HDRC_DEVCTL);
+
+ if (is_on)
+ devctl |= MGC_M_DEVCTL_SESSION;
+ else
+ devctl &= ~MGC_M_DEVCTL_SESSION;
+ musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl);
+ } else
+ is_on = 0;
+
if (is_on) {
+ /* NOTE: assumes VBUS already exceeds A-valid */
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
+ MUSB_HST_MODE(musb);
} else {
- musb->xceiv.state = OTG_STATE_B_IDLE;
+ switch (musb->xceiv.state) {
+ case OTG_STATE_UNDEFINED:
+ case OTG_STATE_B_IDLE:
+ MUSB_DEV_MODE(musb);
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ break;
+ case OTG_STATE_A_IDLE:
+ break;
+ default:
+ musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
+ break;
+ }
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
- musb_writeb(mregs, MGC_O_HDRC_DEVCTL, devctl);
+
+ DBG(2, "Default-%c, VBUS power %s, %s, devctl %02x, %s\n",
+ musb->xceiv.default_a ? 'A' : 'B',
+ is_on ? "on" : "off",
+ MUSB_MODE(musb),
+ musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL),
+ otg_state_string(musb));
}
/* VBUS SWITCHING IS BOARD-SPECIFIC */
#ifdef CONFIG_MACH_DAVINCI_EVM
+#ifndef CONFIG_MACH_DAVINCI_EVM_OTG
/* I2C operations are always synchronous, and require a task context.
* With unloaded systems, using the shared workqueue seems to suffice
static void evm_deferred_drvvbus(void *_musb)
{
struct musb *musb = _musb;
- int is_on = (musb->xceiv.state == OTG_STATE_A_WAIT_VRISE);
+ int is_on = (musb->xceiv.state == OTG_STATE_A_IDLE);
davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on);
vbus_state = is_on;
}
DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus, 0);
-#endif
+#endif /* modified board */
+#endif /* EVM */
-static void davinci_vbus_power(struct musb *musb, int is_on, int sleeping)
+static void davinci_vbus_power(struct musb *musb, int is_on, int immediate)
{
if (is_on)
is_on = 1;
if (vbus_state == is_on)
return;
- if (is_on) {
- musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
- MUSB_HST_MODE(musb);
- } else {
- switch (musb->xceiv.state) {
- case OTG_STATE_UNDEFINED:
- case OTG_STATE_B_IDLE:
- MUSB_DEV_MODE(musb);
- musb->xceiv.state = OTG_STATE_B_IDLE;
- break;
- case OTG_STATE_A_IDLE:
- break;
- default:
- musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
- break;
- }
- }
-
#ifdef CONFIG_MACH_DAVINCI_EVM
if (machine_is_davinci_evm()) {
#ifdef CONFIG_MACH_DAVINCI_EVM_OTG
gpio_set(GPIO(6));
else
gpio_clear(GPIO(6));
+ immediate = 1;
#else
- if (sleeping)
+ if (immediate)
davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on);
else
schedule_work(&evm_vbus_work);
#endif
}
#endif
- if (sleeping) {
+ if (immediate) {
vbus_state = is_on;
session(musb, is_on);
+ } else {
+ /* REVISIT: if is_on, start in A_WAIT_VRISE, then OTG timer
+ * should watch for session valid before calling session().
+ * EVM charges C133 VERY quickly (but discharge is sloooow).
+ */
}
-
- DBG(2, "VBUS power %s, %s\n", is_on ? "on" : "off",
- sleeping ? "immediate" : "deferred");
}
static void davinci_set_vbus(struct musb *musb, int is_on)
{
+ WARN_ON(is_on && is_peripheral_active(musb));
return davinci_vbus_power(musb, is_on, 0);
}
>> DAVINCI_USB_USBINT_SHIFT;
musb->int_regs = r;
+ /* treat DRVVBUS irq like an ID change IRQ (for now) */
if (tmp & (1 << (8 + DAVINCI_USB_USBINT_SHIFT))) {
int drvvbus = musb_readl(tibase, DAVINCI_USB_STAT_REG);
+ if (drvvbus) {
+ MUSB_HST_MODE(musb);
+ musb->xceiv.default_a = 1;
+ musb->xceiv.state = OTG_STATE_A_IDLE;
+ } else {
+ MUSB_DEV_MODE(musb);
+ musb->xceiv.default_a = 0;
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ }
+
/* NOTE: this must complete poweron within 100 msec */
davinci_vbus_power(musb, drvvbus, 0);
- DBG(2, "DRVVBUS %d (state %d)\n", drvvbus, musb->xceiv.state);
+ DBG(2, "DRVVBUS %d (%s)\n", drvvbus, otg_state_string(musb));
retval = IRQ_HANDLED;
}
if (revision == 0)
return -ENODEV;
- /* note that transceiver issues make us want to charge
- * VBUS only when the PHY PLL is not active.
- */
#ifdef CONFIG_MACH_DAVINCI_EVM
- evm_vbus_work.data = musb;
+ if (machine_is_davinci_evm())
+ evm_vbus_work.data = musb;
#endif
- davinci_vbus_power(musb, musb->board_mode == MUSB_HOST, 1);
- if (is_host_enabled(musb))
- musb->board_set_vbus = davinci_set_vbus;
+ musb->board_set_vbus = davinci_set_vbus;
+ davinci_vbus_power(musb, 0, 1);
/* reset the controller */
musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1);
int musb_platform_exit(struct musb *musb)
{
- phy_off();
davinci_vbus_power(musb, 0 /*off*/, 1);
+
+ /* delay, to avoid problems with module reload */
+ if (is_host_enabled(musb)) {
+ int maxdelay = 30;
+ u8 devctl, warn = 0;
+
+ /* if there's no peripheral connected, this can take a
+ * long time to fall, especially on EVM with huge C133.
+ */
+ do {
+ devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);
+ if (!(devctl & MGC_M_DEVCTL_VBUS))
+ break;
+ if ((devctl & MGC_M_DEVCTL_VBUS) != warn) {
+ warn = devctl & MGC_M_DEVCTL_VBUS;
+ DBG(1, "VBUS %d\n", warn >> MGC_S_DEVCTL_VBUS);
+ }
+ msleep(1000);
+ maxdelay--;
+ } while (maxdelay > 0);
+
+ /* in OTG mode, another host might be connected */
+ if (devctl & MGC_M_DEVCTL_VBUS)
+ DBG(1, "VBUS off timeout (devctl %02x)\n", devctl);
+ }
+
+ phy_off();
return 0;
}
* exist tend to be severely undercommitted. You can't yet hook
* up both a keyboard and a mouse to an external USB hub.
*
- * * Host side doesn't understand that hardware endpoints have two
- * directions, so it uses only half the resources available on
- * chips like DaVinci or TUSB 6010.
- *
- * +++ PARTIALLY RESOLVED +++
- *
- * RESULT: On DaVinci (and TUSB 6010), only one external device may
- * use periodic transfers, other than the hub used to connect it.
- * (And if it were to understand, there would still be limitations
- * because of the lack of periodic endpoint scheduling.)
- *
* - Provides its own OTG bits. These are untested, and many of them
* seem to be superfluous code bloat given what usbcore does. (They
* have now been partially removed.)
#ifdef CONFIG_USB_MUSB_HDRC_HCD
/* see manual for the order of the tests */
if (bIntrUSB & MGC_M_INTR_SESSREQ) {
- DBG(1, "SESSION_REQUEST (%d)\n", pThis->xceiv.state);
+ DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(pThis));
/* IRQ arrives from ID pin sense or (later, if VBUS power
* is removed) SRP. responses are time critical:
void musb_start(struct musb *musb)
{
void __iomem *regs = musb->pRegs;
- u8 devctl;
+ u8 devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL);
- DBG(2, "<==\n");
-
- /* TODO: always set ISOUPDATE in POWER (periph mode) and leave it on! */
+ DBG(2, "<== devctl %02x\n", devctl);
/* Set INT enable registers, enable interrupts */
musb_writew(regs, MGC_O_HDRC_INTRTXE, musb->wEndMask);
musb_platform_enable(musb);
- /* enable high-speed/low-power and start session */
- musb_writeb(regs, MGC_O_HDRC_POWER,
- MGC_M_POWER_SOFTCONN | MGC_M_POWER_HSENAB);
+ /* put into basic highspeed mode and start session */
+ musb_writeb(regs, MGC_O_HDRC_POWER, MGC_M_POWER_ISOUPDATE
+ | MGC_M_POWER_SOFTCONN
+ | MGC_M_POWER_HSENAB
+ // | MGC_M_POWER_ENSUSPEND
+ );
musb->is_active = 0;
- switch (musb->board_mode) {
- case MUSB_HOST:
- musb_set_vbus(musb, 1);
- break;
- case MUSB_OTG:
+ devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL);
+ devctl &= ~MGC_M_DEVCTL_SESSION;
+
+ if (is_otg_enabled(pThis)) {
/* session started after:
* (a) ID-grounded irq, host mode;
* (b) vbus present/connect IRQ, peripheral mode;
* (c) peripheral initiates, using SRP
*/
- break;
- case MUSB_PERIPHERAL:
- devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL);
if ((devctl & MGC_M_DEVCTL_VBUS) == MGC_M_DEVCTL_VBUS)
musb->is_active = 1;
- musb_writeb(regs, MGC_O_HDRC_DEVCTL,
- devctl & ~MGC_M_DEVCTL_SESSION);
- break;
+ else
+ devctl |= MGC_M_DEVCTL_SESSION;
+
+ } else if (is_host_enabled(pThis)) {
+ /* assume ID pin is hard-wired to ground */
+ devctl |= MGC_M_DEVCTL_SESSION;
+
+ } else /* peripheral is enabled */ {
+ if ((devctl & MGC_M_DEVCTL_VBUS) == MGC_M_DEVCTL_VBUS)
+ musb->is_active = 1;
}
+ musb_writeb(regs, MGC_O_HDRC_DEVCTL, devctl);
}
break;
case MUSB_OTG:
v1 = "Mini-";
- v2 = (vbus & MGC_M_DEVCTL_BDEVICE) ? "A" : "B";
+ v2 = (vbus & MGC_M_DEVCTL_BDEVICE) ? "B" : "A";
break;
}
} else /* VBUS level below A-Valid */
(void) c->stop(c->pPrivateData);
dma_controller_factory.destroy(c);
}
+
+ musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, 0);
musb_platform_exit(musb);
+ musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, 0);
+
if (musb->clock) {
clk_disable(musb->clock);
clk_put(musb->clock);
#endif
/* For the host-only role, we can activate right away.
+ * (We expect the ID pin to be forcibly grounded!!)
* Otherwise, wait till the gadget driver hooks up.
- *
- * REVISIT switch to compile-time is_role_host() etc
- * to get rid of #ifdeffery
*/
- switch (pThis->board_mode) {
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
- case MUSB_HOST:
+ pThis->xceiv.state = OTG_STATE_B_IDLE;
+ pThis->xceiv.default_a = 0;
+
+ if (is_otg_enabled(pThis)) {
+ MUSB_OTG_MODE(pThis);
+ status = musb_gadget_setup(pThis);
+
+ DBG(1, "%s mode, status %d, dev%02x\n",
+ "OTG", status,
+ musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL));
+
+ } else if (is_host_enabled(pThis)) {
MUSB_HST_MODE(pThis);
+ pThis->xceiv.default_a = 1;
pThis->xceiv.state = OTG_STATE_A_IDLE;
+
status = usb_add_hcd(musb_to_hcd(pThis), -1, 0);
DBG(1, "%s mode, status %d, devctl %02x %c\n",
(musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL)
& MGC_M_DEVCTL_BDEVICE
? 'B' : 'A'));
- break;
-#endif
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
- case MUSB_PERIPHERAL:
+
+ } else /* peripheral is enabled */ {
MUSB_DEV_MODE(pThis);
status = musb_gadget_setup(pThis);
DBG(1, "%s mode, status %d, dev%02x\n",
"PERIPHERAL", status,
musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL));
- break;
-#endif
-#ifdef CONFIG_USB_MUSB_OTG
- case MUSB_OTG:
- MUSB_OTG_MODE(pThis);
- status = musb_gadget_setup(pThis);
- DBG(1, "%s mode, status %d, dev%02x\n",
- "OTG", status,
- musb_readb(pThis->pRegs, MGC_O_HDRC_DEVCTL));
-#endif
- break;
}
if (status == 0)