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 <m...@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.

> ---
>  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?

        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?

> +
> +     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.

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?

Thanks,
Stephan

Reply via email to