Commit 5261debe authored by David Brownell's avatar David Brownell Committed by Tony Lindgren

musb_hdrc: DaVinci-specific updates and cleanups

 - Add header needed for DaVinci build.
 - Remove some partially obsolete comments.
 - Cleaner and more reproducible host side VBUS handling, including
   waiting for it to fall after powerdown on rmmod.
 - Tighten up various state machine transitions and diagnostics

Plus removing some generic #ifdeffery.  The vbus updates make it work more like
the TUSB code, and the cleanups etc make upstream merging more practical.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
parent 652bfff7
......@@ -35,26 +35,26 @@
#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 */
......@@ -68,7 +68,7 @@ static inline void phy_on(void)
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));
}
......@@ -138,26 +138,52 @@ static int vbus_state = -1;
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
......@@ -166,7 +192,7 @@ static void session(struct musb *musb, int is_on)
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;
......@@ -174,9 +200,10 @@ static void evm_deferred_drvvbus(void *_musb)
}
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;
......@@ -184,24 +211,6 @@ static void davinci_vbus_power(struct musb *musb, int is_on, int sleeping)
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
......@@ -212,25 +221,29 @@ static void davinci_vbus_power(struct musb *musb, int is_on, int sleeping)
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);
}
......@@ -281,12 +294,23 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci, struct pt_regs *r)
>> 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;
}
......@@ -331,16 +355,13 @@ int __devinit musb_platform_init(struct musb *musb)
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);
......@@ -362,7 +383,33 @@ int __devinit musb_platform_init(struct musb *musb)
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;
}
......@@ -20,9 +20,9 @@
#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */
#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */
#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */
#define USBPHY_CLK01SEL (1 << 3)
#define USBPHY_CLKO1SEL (1 << 3)
#define USBPHY_OSCPDWN (1 << 2)
#define USBPHY_PHYSPDWN (1 << 0)
#define USBPHY_PHYPDWN (1 << 0)
/* For now include usb OTG module registers here */
#define DAVINCI_USB_VERSION_REG 0x00
......
......@@ -214,15 +214,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
if (is_in)
return;
/* TODO: with CPPI DMA, once DMA is setup and DmaReqEnable in TxCSR
* is set (which is the case) transfer is initiated. For periodic
* transfer support, add another field in pEnd struct which will
* serve as a flag. If CPPI DMA is programmed for the transfer set
* this flag and disable DMAReqEnab while programming TxCSR in
* programEnd() Once we reach the appropriate time, enable DMA Req
* instead of calling musb_h_tx_start() function
*/
/* determine if the time is right for a periodic transfer */
switch (qh->type) {
case USB_ENDPOINT_XFER_ISOC:
......
......@@ -42,6 +42,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h> /* FIXME remove procfs writes */
#include <asm/arch/hardware.h>
#include "musbdefs.h"
......
......@@ -78,17 +78,6 @@
* 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.)
......@@ -416,7 +405,7 @@ static irqreturn_t musb_stage0_irq(struct musb * pThis, u8 bIntrUSB,
#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:
......@@ -660,11 +649,9 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
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);
......@@ -675,30 +662,37 @@ void musb_start(struct musb *musb)
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);
}
......@@ -1514,7 +1508,7 @@ musb_cable_show(struct device *dev, struct device_attribute *attr, char *buf)
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 */
......@@ -1613,7 +1607,11 @@ static void musb_free(struct musb *musb)
(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);
......@@ -1762,16 +1760,25 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
#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",
......@@ -1780,28 +1787,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
(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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment