Newer
Older
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
i < musb->nr_endpoints;
i++, hw_ep++) {
musb_ep_select(musb->mregs, i);
if (hw_ep->is_shared_fifo /* || !epnum */) {
nuke(&hw_ep->ep_in, -ESHUTDOWN);
} else {
if (hw_ep->max_packet_sz_tx)
nuke(&hw_ep->ep_in, -ESHUTDOWN);
if (hw_ep->max_packet_sz_rx)
nuke(&hw_ep->ep_out, -ESHUTDOWN);
}
}
}
}
/*
* Unregister the gadget driver. Used by gadget drivers when
* unregistering themselves from the controller.
*
* @param driver the gadget driver to unregister
*/
static int musb_gadget_stop(struct usb_gadget *g,
struct usb_gadget_driver *driver)
struct musb *musb = gadget_to_musb(g);
Felipe Balbi
committed
unsigned long flags;
if (musb->xceiv->last_event == USB_EVENT_NONE)
pm_runtime_get_sync(musb->controller);
Felipe Balbi
committed
/*
* REVISIT always use otg_set_peripheral() here too;
* this needs to shut down the OTG engine.
*/
spin_lock_irqsave(&musb->lock, flags);
musb_hnp_stop(musb);
Felipe Balbi
committed
(void) musb_gadget_vbus_draw(&musb->g, 0);
Felipe Balbi
committed
musb->xceiv->state = OTG_STATE_UNDEFINED;
stop_activity(musb, driver);
otg_set_peripheral(musb->xceiv->otg, NULL);
dev_dbg(musb->controller, "unregistering driver %s\n", driver->function);
Felipe Balbi
committed
musb->is_active = 0;
musb_platform_try_idle(musb, 0);
spin_unlock_irqrestore(&musb->lock, flags);
Felipe Balbi
committed
if (is_otg_enabled(musb)) {
usb_remove_hcd(musb_to_hcd(musb));
/* FIXME we need to be able to register another
* gadget driver here and have everything work;
* that currently misbehaves.
*/
}
Felipe Balbi
committed
if (!is_otg_enabled(musb))
musb_stop(musb);
pm_runtime_put(musb->controller);
Felipe Balbi
committed
return 0;
}
/* ----------------------------------------------------------------------- */
/* lifecycle operations called through plat_uds.c */
void musb_g_resume(struct musb *musb)
{
musb->is_suspended = 0;
switch (musb->xceiv->state) {
case OTG_STATE_B_IDLE:
break;
case OTG_STATE_B_WAIT_ACON:
case OTG_STATE_B_PERIPHERAL:
musb->is_active = 1;
if (musb->gadget_driver && musb->gadget_driver->resume) {
spin_unlock(&musb->lock);
musb->gadget_driver->resume(&musb->g);
spin_lock(&musb->lock);
}
break;
default:
WARNING("unhandled RESUME transition (%s)\n",
otg_state_string(musb->xceiv->state));
}
}
/* called when SOF packets stop for 3+ msec */
void musb_g_suspend(struct musb *musb)
{
u8 devctl;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "devctl %02x\n", devctl);
switch (musb->xceiv->state) {
case OTG_STATE_B_IDLE:
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
break;
case OTG_STATE_B_PERIPHERAL:
musb->is_suspended = 1;
if (musb->gadget_driver && musb->gadget_driver->suspend) {
spin_unlock(&musb->lock);
musb->gadget_driver->suspend(&musb->g);
spin_lock(&musb->lock);
}
break;
default:
/* REVISIT if B_HOST, clear DEVCTL.HOSTREQ;
* A_PERIPHERAL may need care too
*/
WARNING("unhandled SUSPEND transition (%s)\n",
otg_state_string(musb->xceiv->state));
}
}
/* Called during SRP */
void musb_g_wakeup(struct musb *musb)
{
musb_gadget_wakeup(&musb->g);
}
/* called when VBUS drops below session threshold, and in other cases */
void musb_g_disconnect(struct musb *musb)
{
void __iomem *mregs = musb->mregs;
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "devctl %02x\n", devctl);
/* clear HR */
musb_writeb(mregs, MUSB_DEVCTL, devctl & MUSB_DEVCTL_SESSION);
/* don't draw vbus until new b-default session */
(void) musb_gadget_vbus_draw(&musb->g, 0);
musb->g.speed = USB_SPEED_UNKNOWN;
if (musb->gadget_driver && musb->gadget_driver->disconnect) {
spin_unlock(&musb->lock);
musb->gadget_driver->disconnect(&musb->g);
spin_lock(&musb->lock);
}
switch (musb->xceiv->state) {
dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n",
otg_state_string(musb->xceiv->state));
musb->xceiv->state = OTG_STATE_A_IDLE;
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
break;
case OTG_STATE_B_WAIT_ACON:
case OTG_STATE_B_HOST:
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
musb->xceiv->state = OTG_STATE_B_IDLE;
break;
case OTG_STATE_B_SRP_INIT:
break;
}
musb->is_active = 0;
}
void musb_g_reset(struct musb *musb)
__releases(musb->lock)
__acquires(musb->lock)
{
void __iomem *mbase = musb->mregs;
u8 devctl = musb_readb(mbase, MUSB_DEVCTL);
u8 power;
dev_dbg(musb->controller, "<== %s addr=%x driver '%s'\n",
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
(devctl & MUSB_DEVCTL_BDEVICE)
? "B-Device" : "A-Device",
musb_readb(mbase, MUSB_FADDR),
musb->gadget_driver
? musb->gadget_driver->driver.name
: NULL
);
/* report disconnect, if we didn't already (flushing EP state) */
if (musb->g.speed != USB_SPEED_UNKNOWN)
musb_g_disconnect(musb);
/* clear HR */
else if (devctl & MUSB_DEVCTL_HR)
musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
/* what speed did we negotiate? */
power = musb_readb(mbase, MUSB_POWER);
musb->g.speed = (power & MUSB_POWER_HSMODE)
? USB_SPEED_HIGH : USB_SPEED_FULL;
/* start in USB_STATE_DEFAULT */
musb->is_active = 1;
musb->is_suspended = 0;
MUSB_DEV_MODE(musb);
musb->address = 0;
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
musb->may_wakeup = 0;
musb->g.b_hnp_enable = 0;
musb->g.a_alt_hnp_support = 0;
musb->g.a_hnp_support = 0;
/* Normal reset, as B-Device;
* or else after HNP, as A-Device
*/
if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
musb->g.is_a_peripheral = 0;
} else if (is_otg_enabled(musb)) {
musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
musb->g.is_a_peripheral = 1;
} else
WARN_ON(1);
/* start with default limits on VBUS power draw */
(void) musb_gadget_vbus_draw(&musb->g,
is_otg_enabled(musb) ? 8 : 100);
}