Newer
Older
}
spin_unlock(&musb->lock);
driver->disconnect(&musb->g);
spin_lock(&musb->lock);
}
}
/*
* Unregister the gadget driver. Used by gadget drivers when
* unregistering themselves from the controller.
*
* @param driver the gadget driver to unregister
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct musb *musb = the_gadget;
Felipe Balbi
committed
unsigned long flags;
if (!driver || !driver->unbind || !musb)
return -EINVAL;
Felipe Balbi
committed
if (!musb->gadget_driver)
return -EINVAL;
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);
#ifdef CONFIG_USB_MUSB_OTG
musb_hnp_stop(musb);
#endif
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, NULL);
dev_dbg(musb->controller, "unregistering driver %s\n", driver->function);
Felipe Balbi
committed
spin_unlock_irqrestore(&musb->lock, flags);
driver->unbind(&musb->g);
spin_lock_irqsave(&musb->lock, flags);
Felipe Balbi
committed
musb->gadget_driver = NULL;
musb->g.dev.driver = NULL;
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;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
/* ----------------------------------------------------------------------- */
/* 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:
#endif
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",
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
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
(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);
}