From: Stefan Eichenberger <[email protected]> Register the ChipIdea gadget as a UCLASS_USB_GADGET_GENERIC driver so it can be used on boards with DM_USB_GADGET enabled, which CI_UDC no longer excludes. The driver enables the clocks and the PHY of its device tree node and registers the gadget with usb_add_gadget_udc().
It has no of_match of its own. The controller node is shared with the EHCI host driver, which owns it and binds this driver by name, so CI_UDC depends on that driver once DM_USB_GADGET is enabled. Boards without DM_USB_GADGET keep the existing behaviour. Signed-off-by: Stefan Eichenberger <[email protected]> --- drivers/usb/gadget/Kconfig | 8 +- drivers/usb/gadget/ci_udc.c | 152 +++++++++++++++++++++++++++++++++++- 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index e42d5a43696..67826ee5150 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -153,12 +153,18 @@ config USB_GADGET_OS_DESCRIPTORS config CI_UDC bool "ChipIdea device controller" - depends on !DM_USB_GADGET + depends on !DM_USB_GADGET || USB_EHCI_MX6 || USB_EHCI_MX7 select USB_GADGET_DUALSPEED help Say Y here to enable device controller functionality of the ChipIdea driver. + With DM_USB_GADGET enabled this driver has no device tree match of + its own. The EHCI host driver owns the controller node and binds + this driver by name for the ports whose dr_mode is "peripheral" or + "otg", which is why it is required. Without DM_USB_GADGET the + controller is picked up through the EHCI host driver as before. + config USB_GADGET_VBUS_DRAW int "Maximum VBUS Power usage (2-500 mA)" range 2 500 diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 53796887dac..f63cb17b58f 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -7,14 +7,18 @@ * [email protected], 27-Jan-01. */ +#include <clk.h> #include <command.h> #include <config.h> #include <cpu_func.h> +#include <dm.h> +#include <generic-phy.h> #include <net.h> #include <malloc.h> #include <wait_bit.h> #include <asm/byteorder.h> #include <asm/cache.h> +#include <dm/device_compat.h> #include <linux/delay.h> #include <linux/errno.h> #include <asm/io.h> @@ -23,6 +27,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <usb/ci_udc.h> +#include <usb/ehci-ci.h> #include "../host/ehci.h" #include "ci_udc.h" @@ -93,9 +98,18 @@ static int ci_ep_dequeue(struct usb_ep *ep, struct usb_request *req); 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_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +static int ci_udc_stop(struct usb_gadget *gadget); +#endif static const struct usb_gadget_ops ci_udc_ops = { .pullup = ci_pullup, +#if CONFIG_IS_ENABLED(DM_USB_GADGET) + .udc_start = ci_udc_start, + .udc_stop = ci_udc_stop, +#endif }; static const struct usb_ep_ops ci_ep_ops = { @@ -994,7 +1008,7 @@ void udc_irq(void) } } -int dm_usb_gadget_handle_interrupts(struct udevice *dev) +static int ci_udc_gadget_handle_interrupts(struct udevice *dev) { struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; u32 value; @@ -1139,6 +1153,12 @@ static int ci_udc_probe(void) return 0; } +#if !CONFIG_IS_ENABLED(DM_USB_GADGET) +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + return ci_udc_gadget_handle_interrupts(dev); +} + int usb_gadget_register_driver(struct usb_gadget_driver *driver) { int ret; @@ -1192,6 +1212,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } +#endif /* !DM_USB_GADGET */ bool dfu_usb_get_reset(void) { @@ -1199,3 +1220,132 @@ bool dfu_usb_get_reset(void) return !!(readl(&udc->usbsts) & STS_URI); } + +#if CONFIG_IS_ENABLED(DM_USB_GADGET) +struct ci_udc_priv { + /* Only hccr and hcor are used, the gadget does not drive EHCI. */ + struct ehci_ctrl ctrl; + struct clk_bulk clks; + struct phy phy; +}; + +static int ci_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + if (!driver || !driver->bind || !driver->setup || !driver->disconnect) + return -EINVAL; + + controller.driver = driver; + + return 0; +} + +static int ci_udc_stop(struct usb_gadget *gadget) +{ + controller.driver = NULL; + + return 0; +} + +static int ci_udc_clk_init(struct udevice *dev, struct clk_bulk *clks) +{ + int ret; + + ret = clk_get_bulk(dev, clks); + if (ret == -ENOSYS || ret == -ENOENT) + return 0; + if (ret) + return ret; + + ret = clk_enable_bulk(clks); + if (ret) { + clk_release_bulk(clks); + return ret; + } + + return 0; +} + +static int ci_udc_dm_probe(struct udevice *dev) +{ + struct ci_udc_priv *priv = dev_get_priv(dev); + struct usb_ehci *ehci; + int ret; + + ehci = dev_read_addr_ptr(dev); + if (!ehci) + return -EINVAL; + + ret = ci_udc_clk_init(dev, &priv->clks); + if (ret) + return ret; + + ret = generic_setup_phy(dev, &priv->phy, 0, PHY_MODE_USB_DEVICE, 0); + if (ret) + goto err_clk; + + /* Same register layout the EHCI host driver derives its regs from. */ + priv->ctrl.hccr = (struct ehci_hccr *)&ehci->caplength; + priv->ctrl.hcor = (struct ehci_hcor *)((uintptr_t)priv->ctrl.hccr + + HC_LENGTH(ehci_readl(&priv->ctrl.hccr->cr_capbase))); + controller.ctrl = &priv->ctrl; + + ret = ci_udc_probe(); + if (ret) { + dev_err(dev, "udc probe failed, returned %d\n", ret); + goto err_phy; + } + + ret = usb_add_gadget_udc((struct device *)dev, &controller.gadget); + if (ret) + goto err_udc; + + return 0; + +err_udc: + ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req); + free(controller.items_mem); + free(controller.epts); +err_phy: + generic_shutdown_phy(&priv->phy); +err_clk: + clk_release_bulk(&priv->clks); + return ret; +} + +static int ci_udc_dm_remove(struct udevice *dev) +{ + struct ci_udc_priv *priv = dev_get_priv(dev); + + usb_del_gadget_udc(&controller.gadget); + + ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req); + free(controller.items_mem); + free(controller.epts); + controller.ctrl = NULL; + + generic_shutdown_phy(&priv->phy); + clk_release_bulk(&priv->clks); + + return 0; +} + +static const struct usb_gadget_generic_ops ci_udc_gadget_ops = { + .handle_interrupts = ci_udc_gadget_handle_interrupts, +}; + +/* + * No of_match: the ChipIdea controller shares its device tree node with the + * EHCI host driver, which owns the node and binds this driver by name for + * the ports that are usable in device mode. + */ +U_BOOT_DRIVER(ci_udc) = { + .name = "ci_udc", + .id = UCLASS_USB_GADGET_GENERIC, + .ops = &ci_udc_gadget_ops, + .probe = ci_udc_dm_probe, + .remove = ci_udc_dm_remove, + .priv_auto = sizeof(struct ci_udc_priv), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif /* DM_USB_GADGET */ -- 2.55.0
