Commit 93dc2568 authored by Tony Lindgren's avatar Tony Lindgren Committed by Greg Kroah-Hartman

usb: musb: Add musb_set_host and peripheral and use them for omap2430

At least some revisions of musb core need to set devctl session bit
in peripheral mode to force musb to host mode. And we have places
clearing the devctl session bit.

Let's add a generic function to do this, and use it for omap2430.
This should get us a bit closer to completely removing devctl register
tinkering in the SoC glue code.

Before making use of this code for the other glue layers, things need
to be tested carefully as there may be a approximately a 200 ms delay
needed between powering up musb and calling musb_set_host() to avoid.
Otherwise the system hangs at least with omap2430 glue layer.
Acked-by: default avatarPavel Machek <pavel@ucw.cz>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
[b-liu@ti.com: fixed "line over 80 characters" warning]
Signed-off-by: default avatarBin Liu <b-liu@ti.com>
Link: https://lore.kernel.org/r/20200115132547.364-8-b-liu@ti.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 15f1122f
......@@ -73,6 +73,7 @@
#include <linux/prefetch.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
#include <linux/usb/of.h>
......@@ -414,6 +415,108 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
return hw_ep->musb->io.write_fifo(hw_ep, len, src);
}
static u8 musb_read_devctl(struct musb *musb)
{
return musb_readb(musb->mregs, MUSB_DEVCTL);
}
/**
* musb_set_host - set and initialize host mode
* @musb: musb controller driver data
*
* At least some musb revisions need to enable devctl session bit in
* peripheral mode to switch to host mode. Initializes things to host
* mode and sets A_IDLE. SoC glue needs to advance state further
* based on phy provided VBUS state.
*
* Note that the SoC glue code may need to wait for musb to settle
* on enable before calling this to avoid babble.
*/
int musb_set_host(struct musb *musb)
{
int error = 0;
u8 devctl;
if (!musb)
return -EINVAL;
devctl = musb_read_devctl(musb);
if (!(devctl & MUSB_DEVCTL_BDEVICE)) {
dev_info(musb->controller,
"%s: already in host mode: %02x\n",
__func__, devctl);
goto init_data;
}
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
error = readx_poll_timeout(musb_read_devctl, musb, devctl,
!(devctl & MUSB_DEVCTL_BDEVICE), 5000,
1000000);
if (error) {
dev_err(musb->controller, "%s: could not set host: %02x\n",
__func__, devctl);
return error;
}
init_data:
musb->is_active = 1;
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
return error;
}
EXPORT_SYMBOL_GPL(musb_set_host);
/**
* musb_set_peripheral - set and initialize peripheral mode
* @musb: musb controller driver data
*
* Clears devctl session bit and initializes things for peripheral
* mode and sets B_IDLE. SoC glue needs to advance state further
* based on phy provided VBUS state.
*/
int musb_set_peripheral(struct musb *musb)
{
int error = 0;
u8 devctl;
if (!musb)
return -EINVAL;
devctl = musb_read_devctl(musb);
if (devctl & MUSB_DEVCTL_BDEVICE) {
dev_info(musb->controller,
"%s: already in peripheral mode: %02x\n",
__func__, devctl);
goto init_data;
}
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
error = readx_poll_timeout(musb_read_devctl, musb, devctl,
devctl & MUSB_DEVCTL_BDEVICE, 5000,
1000000);
if (error) {
dev_err(musb->controller, "%s: could not set periperal: %02x\n",
__func__, devctl);
return error;
}
init_data:
musb->is_active = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
return error;
}
EXPORT_SYMBOL_GPL(musb_set_peripheral);
/*-------------------------------------------------------------------------*/
/* for high speed test mode; see USB 2.0 spec 7.1.20 */
......
......@@ -487,6 +487,9 @@ extern void musb_start(struct musb *musb);
extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
extern int musb_set_host(struct musb *musb);
extern int musb_set_peripheral(struct musb *musb);
extern void musb_load_testpacket(struct musb *);
extern irqreturn_t musb_interrupt(struct musb *);
......
......@@ -38,65 +38,38 @@ struct omap2430_glue {
static struct omap2430_glue *_glue;
/*
* HDRC controls CPEN, but beware current surges during device connect.
* They can trigger transient overcurrent conditions that must be ignored.
*
* Note that we're skipping A_WAIT_VFALL -> A_IDLE and jumping right to B_IDLE
* as set by musb_set_peripheral().
*/
static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
{
struct usb_otg *otg = musb->xceiv->otg;
u8 devctl;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
/* HDRC controls CPEN, but beware current surges during device
* connect. They can trigger transient overcurrent conditions
* that must be ignored.
*/
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
int error;
if (is_on) {
if (musb->xceiv->otg->state == OTG_STATE_A_IDLE) {
int loops = 100;
/* start the session */
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
/*
* Wait for the musb to set as A device to enable the
* VBUS
*/
while (musb_readb(musb->mregs, MUSB_DEVCTL) &
MUSB_DEVCTL_BDEVICE) {
mdelay(5);
cpu_relax();
if (time_after(jiffies, timeout)
|| loops-- <= 0) {
dev_err(musb->controller,
"configured as A device timeout");
break;
}
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_IDLE:
error = musb_set_host(musb);
if (!error) {
musb->xceiv->otg->state =
OTG_STATE_A_WAIT_VRISE;
otg_set_vbus(otg, 1);
}
break;
default:
otg_set_vbus(otg, 1);
} else {
musb->is_active = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
break;
}
} else {
musb->is_active = 0;
/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
* jumping right to B_IDLE...
*/
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
devctl &= ~MUSB_DEVCTL_SESSION;
MUSB_DEV_MODE(musb);
error = musb_set_peripheral(musb);
otg_set_vbus(otg, 0);
}
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
dev_dbg(musb->controller, "VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ "\n",
dev_dbg(musb->controller, "VBUS %s, devctl %02x\n",
usb_otg_state_string(musb->xceiv->otg->state),
musb_readb(musb->mregs, MUSB_DEVCTL));
}
......
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