[PATCH 1/3] usb: udc: ci: support USB gadget driver model

Wojciech Dubowik wojciech.dubowik at mt.com
Wed Jun 4 09:07:04 CEST 2025


On Sat, Apr 19, 2025 at 09:32:04AM +0000, Sam Day wrote:
> Heya Stephan,
> 
> On Wed Apr 16, 2025 at 8:51 PM CEST, Stephan Gerhold wrote:
> > On Sat, Apr 12, 2025 at 07:39:57PM +0000, Sam Day wrote:
> >> When CONFIG_DM_USB_GADGET is enabled, a UCLASS_USB_GADGET_GENERIC driver
> >> will be defined that wraps the ChipIdea UDC operations. The
> >> (dm_)?usb_gadget_.* symbols will no longer be defined (as these are now
> >> handled by the UDC uclass).
> >>
> >> If CONFIG_DM_USB_GADGET is not enabled, this driver behaves as it
> >> previously did.
> >>
> >> This new driver does not declare any compatibles of its own. It requires
> >> a glue driver to bind it. The ehci_msm driver will be updated in the
> >> following commit to do exactly that.
> >>
> >> Signed-off-by: Sam Day <me at samcday.com>
> >
> > Thanks a lot for working on this. Seems to work without problems on my
> > dragonboard410c. Just some minor nits below to reduce code duplication.
> 
> \0/ Thanks for the review and testing!
> 
> >
> >> ---
> >>  drivers/usb/gadget/ci_udc.c | 84 ++++++++++++++++++++++++++++++++++++++++++++-
> >>  1 file changed, 83 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
> >> index 4bff75da759ded6716641713fbe47f6ba79386dc..179f8540da30c693d11d772aacc00cc37f7669c5 100644
> >> --- a/drivers/usb/gadget/ci_udc.c
> >> +++ b/drivers/usb/gadget/ci_udc.c
> >> @@ -10,6 +10,7 @@
> >>  #include <command.h>
> >>  #include <config.h>
> >>  #include <cpu_func.h>
> >> +#include <dm.h>
> >>  #include <net.h>
> >>  #include <malloc.h>
> >>  #include <wait_bit.h>
> >> @@ -94,8 +95,18 @@ static struct usb_request *
> >>  ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags);
> >>  static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req);
> >>
> >> +#if CONFIG_IS_ENABLED(DM_USB_GADGET)
> >> +static int ci_udc_gadget_start(struct usb_gadget *g,
> >> +		struct usb_gadget_driver *driver);
> >> +static int ci_udc_gadget_stop(struct usb_gadget *g);
> >> +#endif
> >> +
> >>  static const struct usb_gadget_ops ci_udc_ops = {
> >>  	.pullup = ci_pullup,
> >> +#if CONFIG_IS_ENABLED(DM_USB_GADGET)
> >> +	.udc_start = ci_udc_gadget_start,
> >> +	.udc_stop = ci_udc_gadget_stop,
> >> +#endif
> >>  };
> >>
> >>  static const struct usb_ep_ops ci_ep_ops = {
> >> @@ -927,7 +938,7 @@ void udc_irq(void)
> >>  	}
> >>  }
> >>
> >> -int dm_usb_gadget_handle_interrupts(struct udevice *dev)
> >> +static int ci_udc_handle_interrupts(struct udevice *dev)
> >>  {
> >>  	struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
> >>  	u32 value;
> >> @@ -1072,6 +1083,71 @@ static int ci_udc_probe(void)
> >>  	return 0;
> >>  }
> >>
> >> +#if CONFIG_IS_ENABLED(DM_USB_GADGET)
> >> +static int ci_udc_generic_probe(struct udevice *dev)
> >> +{
> >> +	int ret;
> >> +#if CONFIG_IS_ENABLED(DM_USB)
> >> +	ret = usb_setup_ehci_gadget(&controller.ctrl);
> >> +#else
> >> +	ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl);
> >> +#endif
> >
> > ...
> >
> >> [...]
> >> +static int ci_udc_generic_remove(struct udevice *dev)
> >> +{
> >> +	usb_del_gadget_udc(&controller.gadget);
> >> +
> >> +	if (IS_ENABLED(CONFIG_DM_USB)) {
> >> +		usb_remove_ehci_gadget(&controller.ctrl);
> >> +	} else {
> >> +		usb_lowlevel_stop(0);
> >> +		controller.ctrl = NULL;
> >> +	}
> >
> > This style is nice for compile testing. But in the probe() function
> > above you're still using the #if/#else pre-processor style. Can it use
> > if (IS_ENABLED(...)) too?
> 
> Ah, I had converted a few of these when checkpatch reprimanded me. Guess
> I missed this one - oops!
> 
> >
> > 	if (IS_ENABLED(CONFIG_DM_USB))
> > 		ret = usb_setup_ehci_gadget(&controller.ctrl);
> > 	else
> > 		ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl);
> > 	if (ret)
> > 		return ret;
> >
> > This seems to compile at least.
> >
> > If yes, can you make the same cleanup for the old !DM_USB_GADGET case,
> > so the two code paths are consistent?
> 
> Thanks, I've done this in v2.
> 
> >
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static const struct usb_gadget_generic_ops ci_udc_generic_ops = {
> >> +	.handle_interrupts	= ci_udc_handle_interrupts,
> >> +};
> >> +
> >> +U_BOOT_DRIVER(ci_udc_generic) = {
> >> +	.name = "ci-udc",
> >> +	.id = UCLASS_USB_GADGET_GENERIC,
> >> +	.ops = &ci_udc_generic_ops,
> >> +	.probe = ci_udc_generic_probe,
> >> +	.remove = ci_udc_generic_remove,
> >> +};
> >> +
> >> +static int ci_udc_gadget_start(struct usb_gadget *g,
> >> +				 struct usb_gadget_driver *driver)
> >> +{
> >> +	controller.driver = driver;
> >> +	return 0;
> >> +}
> >> +
> >> +static int ci_udc_gadget_stop(struct usb_gadget *g)
> >> +{
> >> +	controller.driver = NULL;
> >> +	udc_disconnect();
> >> +
> >> +	ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
> >> +	free(controller.items_mem);
> >> +	free(controller.epts);
> >> +
> >> +	return 0;
> >> +}
> >
> > I wonder if you could just define the start and stop function outside
> > the #if blocks and call them from usb_gadget_unregister_driver()
> > and usb_gadget_register_driver(). This would avoid duplicating the code,
> > reducing the risk that someone forgets to update one of those when
> > making changes.
> 
> So, not quite unfortunately. In the case of ci_udc_gadget_stop(), the
> controller.driver = NULL happening before calling into udc_disconnect()
> is important: the generic UDC uclass code has already called
> driver->disconnect() and calling it again is a Very Bad Thing to do. So
> we can't just delegate usb_gadget_unregister_driver to
> ci_udc_gadget_stop outright.
> 
> In v2 I've tried to unify the codepaths as much as possible though,
> both to avoid duplication and the risks of future patches making
> incomplete changes.
> 
> >
> > The same could be also said for probe()/remove(), i.e. moving the
> >
> > 	if (IS_ENABLED(CONFIG_DM_USB)) {
> > 		usb_remove_ehci_gadget(&controller.ctrl);
> > 	} else {
> > 		usb_lowlevel_stop(0);
> > 		controller.ctrl = NULL;
> > 	}
> >
> > block to a separate function and calling that from both variants. Not
> > sure if that would also be worth it?
> 
> Good call, these ones are less problematic, and I've done that in v2.
Hello Sam,

Are you going to send v2 soon? I am just asking as I had something similar
in the pipeline.

Regrads,
Wojtek

> 
> Rock on \m/,
> -Sam
> 
> >
> > Thanks,
> > Stephan
> 
> 


More information about the U-Boot mailing list