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

Reply via email to