[RFC] usb: musb: dsps, OTG detection

Markus Pargmann mpa at pengutronix.de
Tue Oct 15 03:55:39 EDT 2013


On Mon, Oct 14, 2013 at 03:43:35PM -0500, Bin Liu wrote:
> On Mon, Oct 14, 2013 at 10:22 AM, Markus Pargmann <mpa at pengutronix.de> wrote:
> > Hi,
> >
> > On Mon, Oct 14, 2013 at 08:54:09AM -0500, Bin Liu wrote:
> >> On Mon, Oct 14, 2013 at 8:35 AM, Markus Pargmann <mpa at pengutronix.de> wrote:
> >> > The USB Controller does not support ID pin change interrupts. So we have
> >> > to use a polling function to detect changes of A/B device state
> >> > (otg_timer). This poll function has to check in several states if a
> >> > other device type might be connected to the USB port. This check is
> >> > triggered by manually starting/stopping a USB Session.
> >>
> >> I think this is an arguable approach. Toggling the SESSION in
> >> otg_timer() causes voltage pulses on VBUS, which will not pass the USB
> >> certification.
> >
> > This is only done when no device is connected, so I am not sure if it is
> > important. Unfortunately we do not see the A/B state changes until
> 
> That is right, and I don't think it hurts. The only problem is that
> the USB certification test sees the VBUS pulses and fails.
> 
> > toggling the SESSION. Is there another way to check this?
> 
> Unfortunately, toggling SESSION in b_idle is the only way I am aware of.
> 
> >
> >>
> >> I have not seen any products required the dynamic dual role switching
> >> yet. It always fixed in either device mode or host mode.
> >
> > OTG is explicitly listed in the devicetree bindings documentation, so
> > I think the driver should be able to detect different roles.
> 
> Yes, MUSB supports OTG. But I have not seen anyone use OTG yet, and I
> not sure if it is a good idea to add the OTG support, but fail the usb
> certification test.

For example beaglebone does not overwrite the dr_mode = "otg"; property
of am33xx.dtsi and it is a device where OTG could be useful. You may
want to connect beaglebone to a PC or a keyboard while the other usb
port has a USB storage device attached. With the current implementation
this is only possible if the host or device is connected when the driver
is probing.

I could limit the SESSION toggling to OTG mode. If someone needs a
system that passes the USB certification test, he could simply set the
correct dr_mode in the DT and the device would pass the test.

Regards,

Markus Pargmann

> 
> Regards,
> -Bin.
> 
> >
> > Regards,
> >
> > Markus Pargmann
> >
> >>
> >> Regards,
> >> -Bin.
> >>
> >> >
> >> > So in A mode, we cancel the currently running session which also
> >> > disables the possibility to detect new devices via interrupt. In B mode,
> >> > we start a session to check for ID-Pin and possibly connected devices.
> >> >
> >> > Whenever a real USB session ends, we have to trigger the otg_timer poll
> >> > function again.
> >> >
> >> > Signed-off-by: Markus Pargmann <mpa at pengutronix.de>
> >> > ---
> >> >  drivers/usb/musb/musb_dsps.c | 84 ++++++++++++++++++++++++++++++++++++++++----
> >> >  1 file changed, 78 insertions(+), 6 deletions(-)
> >> >
> >> > diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
> >> > index b24b697..0245e8d 100644
> >> > --- a/drivers/usb/musb/musb_dsps.c
> >> > +++ b/drivers/usb/musb/musb_dsps.c
> >> > @@ -178,6 +178,43 @@ static const struct file_operations musb_regdump_fops = {
> >> >
> >> >  #endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
> >> >
> >> > +/*
> >> > + * Compare driver and hardware mode and update driver state if necessary.
> >> > + * Not all hardware changes actually reach the driver through interrupts.
> >> > + */
> >> > +static void dsps_update_mode(struct musb *musb)
> >> > +{
> >> > +       u8 devctl;
> >> > +
> >> > +       devctl = dsps_readb(musb->mregs, MUSB_DEVCTL);
> >> > +
> >> > +       switch (musb->xceiv->state) {
> >> > +       case OTG_STATE_A_IDLE:
> >> > +               if (devctl & MUSB_DEVCTL_BDEVICE) {
> >> > +                       dev_dbg(musb->controller, "detected controller state B, software state A\n");
> >> > +                       musb->xceiv->state = OTG_STATE_B_IDLE;
> >> > +               }
> >> > +               break;
> >> > +       case OTG_STATE_B_IDLE:
> >> > +               if (!(devctl & MUSB_DEVCTL_BDEVICE)) {
> >> > +                       dev_dbg(musb->controller, "detected controller state A, software state B\n");
> >> > +                       musb->xceiv->state = OTG_STATE_A_IDLE;
> >> > +               }
> >> > +               break;
> >> > +       default:
> >> > +               if (!(devctl & MUSB_DEVCTL_SESSION)) {
> >> > +                       dev_dbg(musb->controller, "detected controller out of session (%x), software state %s\n",
> >> > +                               devctl,
> >> > +                               usb_otg_state_string(musb->xceiv->state));
> >> > +                       if (devctl & MUSB_DEVCTL_BDEVICE)
> >> > +                               musb->xceiv->state = OTG_STATE_B_IDLE;
> >> > +                       else
> >> > +                               musb->xceiv->state = OTG_STATE_A_IDLE;
> >> > +               }
> >> > +               break;
> >> > +       }
> >> > +}
> >> > +
> >> >  /**
> >> >   * dsps_musb_enable - enable interrupts
> >> >   */
> >> > @@ -229,6 +266,8 @@ static void otg_timer(unsigned long _musb)
> >> >         u8 devctl;
> >> >         unsigned long flags;
> >> >
> >> > +       dsps_update_mode(musb);
> >> > +
> >> >         /*
> >> >          * We poll because DSPS IP's won't expose several OTG-critical
> >> >          * status change events (from the transceiver) otherwise.
> >> > @@ -239,6 +278,16 @@ static void otg_timer(unsigned long _musb)
> >> >
> >> >         spin_lock_irqsave(&musb->lock, flags);
> >> >         switch (musb->xceiv->state) {
> >> > +       case OTG_STATE_A_IDLE:
> >> > +       case OTG_STATE_A_WAIT_VRISE:
> >> > +               /*
> >> > +                * Poll the devctl register to know when the controller switches
> >> > +                * back to B state.
> >> > +                */
> >> > +               musb_writeb(mregs, MUSB_DEVCTL,
> >> > +                               devctl & (~MUSB_DEVCTL_SESSION));
> >> > +               mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
> >> > +               break;
> >> >         case OTG_STATE_A_WAIT_BCON:
> >> >                 devctl &= ~MUSB_DEVCTL_SESSION;
> >> >                 dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl);
> >> > @@ -251,6 +300,8 @@ static void otg_timer(unsigned long _musb)
> >> >                         musb->xceiv->state = OTG_STATE_A_IDLE;
> >> >                         MUSB_HST_MODE(musb);
> >> >                 }
> >> > +               mod_timer(&glue->timer,
> >> > +                               jiffies + wrp->poll_seconds * HZ);
> >> >                 break;
> >> >         case OTG_STATE_A_WAIT_VFALL:
> >> >                 musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
> >> > @@ -258,12 +309,24 @@ static void otg_timer(unsigned long _musb)
> >> >                             MUSB_INTR_VBUSERROR << wrp->usb_shift);
> >> >                 break;
> >> >         case OTG_STATE_B_IDLE:
> >> > +               /*
> >> > +                * There's no ID-changed IRQ, so we have no good way to tell
> >> > +                * when to switch to the A-Default state machine (by setting
> >> > +                * the DEVCTL.Session bit).
> >> > +                *
> >> > +                * Workaround:  whenever we're in B_IDLE, try setting the
> >> > +                * session flag every few seconds.  If it works, ID was
> >> > +                * grounded and we're now in the A-Default state machine.
> >> > +                *
> >> > +                * NOTE: setting the session flag is _supposed_ to trigger
> >> > +                * SRP but clearly it doesn't.
> >> > +                */
> >> > +               musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION);
> >> >                 devctl = dsps_readb(mregs, MUSB_DEVCTL);
> >> > -               if (devctl & MUSB_DEVCTL_BDEVICE)
> >> > -                       mod_timer(&glue->timer,
> >> > -                                       jiffies + wrp->poll_seconds * HZ);
> >> > -               else
> >> > +               if (!(devctl & MUSB_DEVCTL_BDEVICE))
> >> >                         musb->xceiv->state = OTG_STATE_A_IDLE;
> >> > +               mod_timer(&glue->timer,
> >> > +                               jiffies + wrp->poll_seconds * HZ);
> >> >                 break;
> >> >         default:
> >> >                 break;
> >> > @@ -376,7 +439,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
> >> >                         MUSB_HST_MODE(musb);
> >> >                         musb->xceiv->otg->default_a = 1;
> >> >                         musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
> >> > -                       del_timer(&glue->timer);
> >> >                 } else {
> >> >                         musb->is_active = 0;
> >> >                         MUSB_DEV_MODE(musb);
> >> > @@ -397,8 +459,16 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
> >> >                 ret |= musb_interrupt(musb);
> >> >
> >> >         /* Poll for ID change */
> >> > -       if (musb->xceiv->state == OTG_STATE_B_IDLE)
> >> > +       switch (musb->xceiv->state) {
> >> > +       case OTG_STATE_A_IDLE:
> >> > +       case OTG_STATE_A_WAIT_BCON:
> >> > +       case OTG_STATE_A_WAIT_VRISE:
> >> > +       case OTG_STATE_B_IDLE:
> >> >                 mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
> >> > +               break;
> >> > +       default:
> >> > +               break;
> >> > +       }
> >> >  out:
> >> >         spin_unlock_irqrestore(&musb->lock, flags);
> >> >
> >> > @@ -479,6 +549,8 @@ static int dsps_musb_init(struct musb *musb)
> >> >
> >> >         dev_info(dev, "%s:%d %s: OK\n", __FILE__, __LINE__, __func__);
> >> >
> >> > +       musb->xceiv->otg->default_a = 0;
> >> > +
> >> >         return 0;
> >> >  }
> >> >
> >> > --
> >> > 1.8.4.rc3
> >> >
> >> > --
> >> > To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> >> > the body of a message to majordomo at vger.kernel.org
> >> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >>
> >
> > --
> > Pengutronix e.K.                           |                             |
> > Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> > Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> > Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the linux-arm-kernel mailing list