From: Sherry Sun <[email protected]> Convert the ci_udc driver to driver model by using the uclass UCLASS_USB_GADGET_GENERIC. The clk and power of USB controller and USB PHY both are initialized by parsing the device tree nodes.
If CONFIG_DM_USB_GADGET is defined, we use the ci_udc driver in DM way, if it does not defined, we can use ci_udc driver in its original Non-DM way. Signed-off-by: Sherry Sun <[email protected]> Signed-off-by: Alice Guo <[email protected]> Reviewed-by: Ye Li <[email protected]> --- drivers/usb/gadget/ci_udc.c | 352 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 348 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 4bff75da759..154a3ab7f22 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -7,9 +7,17 @@ * [email protected], 27-Jan-01. */ +#include <asm/arch/sys_proto.h> +#include <asm/arch/clock.h> +#include <asm/mach-imx/regs-usbphy.h> +#include <clk.h> #include <command.h> #include <config.h> #include <cpu_func.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/pinctrl.h> +#include <power-domain.h> #include <net.h> #include <malloc.h> #include <wait_bit.h> @@ -22,6 +30,8 @@ #include <linux/types.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <usb/ehci-ci.h> #include <usb/ci_udc.h> #include "../host/ehci.h" #include "ci_udc.h" @@ -93,9 +103,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_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 +946,7 @@ void udc_irq(void) } } -int dm_usb_gadget_handle_interrupts(struct udevice *dev) +int ci_udc_handle_interrupts(void) { struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; u32 value; @@ -1072,6 +1091,19 @@ static int ci_udc_probe(void) return 0; } +bool dfu_usb_get_reset(void) +{ + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + + return !!(readl(&udc->usbsts) & STS_URI); +} + +#if !CONFIG_IS_ENABLED(DM_USB_GADGET) +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + return ci_udc_handle_interrupts(); +} + int usb_gadget_register_driver(struct usb_gadget_driver *driver) { int ret; @@ -1125,10 +1157,322 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } +#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */ -bool dfu_usb_get_reset(void) +static int ci_udc_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + if (!driver) + return -EINVAL; + if (!driver->bind || !driver->setup || !driver->disconnect) + return -EINVAL; - return !!(readl(&udc->usbsts) & STS_URI); + controller.driver = driver; + return 0; +} + +static int ci_udc_gadget_stop(struct usb_gadget *g) +{ + controller.driver = NULL; + + ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req); + free(controller.items_mem); + free(controller.epts); + return 0; +} + +struct ci_udc_priv_data { + struct ehci_ctrl ctrl; + struct udevice otgdev; + struct clk_bulk clks; + int phy_off; + struct power_domain otg_pd; + struct clk phy_clk; + struct power_domain phy_pd; + struct ehci_mx6_phy_data phy_data; +}; + +static int ci_udc_gadget_handle_interrupts(struct udevice *dev) +{ + return ci_udc_handle_interrupts(); +} + +static int ci_udc_phy_setup(struct udevice *dev, struct ci_udc_priv_data *priv) +{ + void *__iomem addr; + int misc_off; + struct udevice __maybe_unused phy_dev; + + priv->phy_off = fdtdec_lookup_phandle(gd->fdt_blob, + dev_of_offset(dev), + "fsl,usbphy"); + if (priv->phy_off < 0) { + priv->phy_off = fdtdec_lookup_phandle(gd->fdt_blob, + dev_of_offset(dev), "phys"); + if (priv->phy_off < 0) + return -EINVAL; + } + + addr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, priv->phy_off, "reg"); + if ((fdt_addr_t)addr == FDT_ADDR_T_NONE) + addr = NULL; + + priv->phy_data.phy_addr = addr; + + misc_off = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev), "fsl,usbmisc"); + if (misc_off < 0) + return -EINVAL; + + addr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, misc_off, "reg"); + if ((fdt_addr_t)addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->phy_data.misc_addr = addr; + +#if defined(CONFIG_MX6) + int anatop_off; + + /* Resolve ANATOP offset through USB PHY node */ + anatop_off = fdtdec_lookup_phandle(gd->fdt_blob, priv->phy_off, "fsl,anatop"); + if (anatop_off < 0) + return -EINVAL; + + addr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, anatop_off, "reg"); + if ((fdt_addr_t)addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->phy_data.anatop_addr = addr; +#endif + + dev_set_ofnode(&phy_dev, offset_to_ofnode(priv->phy_off)); + +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + /* Need to power on the PHY before access it */ + if (!power_domain_get(&phy_dev, &priv->phy_pd)) { + if (power_domain_on(&priv->phy_pd)) + return -EINVAL; + } +#endif + +#if CONFIG_IS_ENABLED(CLK) + int ret; + + ret = clk_get_by_index(&phy_dev, 0, &priv->phy_clk); + if (ret) { + printf("Failed to get phy_clk\n"); + return ret; + } + + ret = clk_enable(&priv->phy_clk); + if (ret) { + printf("Failed to enable phy_clk\n"); + return ret; + } +#endif + + return 0; } + +static int ci_udc_phy_shutdown(struct ci_udc_priv_data *priv) +{ + int ret = 0; + +#if CONFIG_IS_ENABLED(CLK) + if (priv->phy_clk.dev) { + ret = clk_disable(&priv->phy_clk); + if (ret) + return ret; + } +#endif + +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + ret = power_domain_off(&priv->phy_pd); + if (ret) + printf("Power down USB PHY failed! (error = %d)\n", ret); +#endif + return ret; +} + +static int ci_udc_otg_clk_init(struct udevice *dev, + struct clk_bulk *clks) +{ +#if CONFIG_IS_ENABLED(CLK) + int ret; + + ret = clk_get_bulk(dev, clks); + if (ret == -ENOSYS) + return 0; + + if (ret) + return ret; + + ret = clk_enable_bulk(clks); + if (ret) { + clk_release_bulk(clks); + return ret; + } +#else + enable_usboh3_clk(1); +#endif + + return 0; +} + +static int ci_udc_otg_phy_mode(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + + void *__iomem phy_ctrl, *__iomem phy_status; + void *__iomem phy_base = (void *__iomem)devfdt_get_addr(&priv->otgdev); + u32 val; + + if (is_mx6() || is_mx7ulp() || is_imx8()) { + phy_base = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, + priv->phy_off, + "reg"); + if ((fdt_addr_t)phy_base == FDT_ADDR_T_NONE) + return -EINVAL; + + phy_ctrl = (void __iomem *)(phy_base + USBPHY_CTRL); + val = readl(phy_ctrl); + if (val & USBPHY_CTRL_OTG_ID) + return USB_INIT_DEVICE; + else + return USB_INIT_HOST; + } else if (is_mx7() || is_imx8mm() || is_imx8mn()) { + phy_status = (void __iomem *)(phy_base + + USBNC_PHY_STATUS_OFFSET); + val = readl(phy_status); + if (val & USBNC_PHYSTATUS_ID_DIG) + return USB_INIT_DEVICE; + else + return USB_INIT_HOST; + } else { + return -EINVAL; + } +} + +static int ci_udc_otg_ofdata_to_platdata(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + int node = dev_of_offset(dev); + int usbotg_off; + + if (usb_get_dr_mode(dev_ofnode(dev)) != USB_DR_MODE_PERIPHERAL) { + dev_dbg(dev, "Invalid mode\n"); + return -ENODEV; + } + + usbotg_off = fdtdec_lookup_phandle(gd->fdt_blob, + node, + "chipidea,usb"); + if (usbotg_off < 0) + return -EINVAL; + dev_set_ofnode(&priv->otgdev, offset_to_ofnode(usbotg_off)); + priv->otgdev.parent = dev->parent; + + return 0; +} + +static int ci_udc_otg_probe(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + struct usb_ehci *ehci; + int ret; + + ehci = (struct usb_ehci *)devfdt_get_addr(&priv->otgdev); + + pinctrl_select_state(&priv->otgdev, "default"); + +#if defined(CONFIG_MX6) + if (usb_fused((u32)ehci)) { + printf("USB@0x%x is fused, disable it\n", (u32)ehci); + return -ENODEV; + } +#endif + + ret = board_usb_init(dev_seq(dev), USB_INIT_DEVICE); + if (ret) { + printf("Failed to initialize board for USB\n"); + return ret; + } + +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + if (!power_domain_get(&priv->otgdev, &priv->otg_pd)) { + if (power_domain_on(&priv->otg_pd)) + return -EINVAL; + } +#endif + + ret = ci_udc_phy_setup(&priv->otgdev, priv); + if (ret) + return ret; + + ret = ci_udc_otg_clk_init(&priv->otgdev, &priv->clks); + if (ret) + return ret; + + ehci_mx6_phy_init(ehci, &priv->phy_data, dev_seq(dev)); + + if (ci_udc_otg_phy_mode(dev) != USB_INIT_DEVICE) + return -ENODEV; + + priv->ctrl.hccr = (struct ehci_hccr *)((ulong)&ehci->caplength); + priv->ctrl.hcor = (struct ehci_hcor *)((ulong)priv->ctrl.hccr + + HC_LENGTH(ehci_readl(&(priv->ctrl.hccr)->cr_capbase))); + controller.ctrl = &priv->ctrl; + + ret = ci_udc_probe(); + if (ret) { + DBG("udc probe failed, returned %d\n", ret); + return ret; + } + + ret = usb_add_gadget_udc((struct device *)dev, &controller.gadget); + + return ret; +} + +static int ci_udc_otg_remove(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + + usb_del_gadget_udc(&controller.gadget); + +#if CONFIG_IS_ENABLED(CLK) + clk_release_bulk(&priv->clks); +#endif + ci_udc_phy_shutdown(priv); +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + if (power_domain_off(&priv->otg_pd)) { + printf("Power down USB controller failed!\n"); + return -EINVAL; + } +#endif + board_usb_cleanup(dev_seq(dev), USB_INIT_DEVICE); + + controller.ctrl = NULL; + return 0; +} + +static const struct udevice_id ci_udc_otg_ids[] = { + { .compatible = "fsl,imx27-usb-gadget" }, + { } +}; + +static const struct usb_gadget_generic_ops ci_udc_gadget_ops = { + .handle_interrupts = ci_udc_gadget_handle_interrupts, +}; + +U_BOOT_DRIVER(ci_udc_otg) = { + .name = "ci-udc-otg", + .id = UCLASS_USB_GADGET_GENERIC, + .of_match = ci_udc_otg_ids, + .of_to_plat = ci_udc_otg_ofdata_to_platdata, + .probe = ci_udc_otg_probe, + .remove = ci_udc_otg_remove, + .ops = &ci_udc_gadget_ops, + .priv_auto = sizeof(struct ci_udc_priv_data), +}; + +#endif /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */ -- 2.43.0

