MLK-22045-3 cdns3: Add cdns3_generic_peripheral DM gadget driver
authorSherry Sun <sherry.sun@nxp.com>
Wed, 19 Jun 2019 20:44:15 +0000 (16:44 -0400)
committerSherry Sun <sherry.sun@nxp.com>
Thu, 27 Jun 2019 18:55:38 +0000 (14:55 -0400)
Add cdns3_generic_peripheral DM gadget driver for i.MX8QM and QXP.

The driver flag is set to CONFIG_DM_USB_GADGET and
CONFIG_SPL_DM_USB_GADGET while using SPL, and the driver is belong to
UCLASS_USB_GADGET_GENERIC uclass.

For the DM gadget driver, it will parse the reg, clocks and usbphy
properties from node in DTS. So the power and clks of cdns3 controller
and cdns3 phy will be both enabled in DM gadget driver.

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
drivers/usb/cdns3/Makefile
drivers/usb/cdns3/cdns3-generic.c [new file with mode: 0644]
drivers/usb/cdns3/core.c
drivers/usb/cdns3/core.h
drivers/usb/cdns3/gadget.c
drivers/usb/cdns3/gadget.h

index c7a04e6..374fa06 100644 (file)
@@ -2,3 +2,4 @@ obj-$(CONFIG_USB_CDNS3)         += cdns3.o
 
 cdns3-y                                := core.o
 cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o
+cdns3-$(CONFIG_$(SPL_)DM_USB_GADGET)   += cdns3-generic.o
diff --git a/drivers/usb/cdns3/cdns3-generic.c b/drivers/usb/cdns3/cdns3-generic.c
new file mode 100644 (file)
index 0000000..0ecb7bf
--- /dev/null
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 NXP
+ */
+
+#include <common.h>
+#include <asm-generic/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <linux/usb/gadget.h>
+#include <malloc.h>
+#include <usb.h>
+#include "core.h"
+#include "gadget.h"
+#include <clk.h>
+#include <power-domain.h>
+
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+
+static int cdns3_setup_phy(struct udevice *dev,
+                          struct cdns3_generic_peripheral *priv)
+{
+       int ret = 0;
+
+#if CONFIG_IS_ENABLED(POWER_DOMAIN)
+       struct udevice phy_dev;
+       int phy_off;
+
+       phy_off = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
+                                       "cdns3,usbphy");
+       if (phy_off < 0)
+               return -EINVAL;
+
+       phy_dev.node = offset_to_ofnode(phy_off);
+
+       if (!power_domain_get(&phy_dev, &priv->phy_pd)) {
+               if (power_domain_on(&priv->phy_pd))
+                       return -EINVAL;
+       }
+#endif
+
+#if CONFIG_IS_ENABLED(CLK)
+       ret = clk_get_by_name(&phy_dev, "main_clk", &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 ret;
+}
+
+static int cdns3_shutdown_phy(struct udevice *dev,
+                             struct cdns3_generic_peripheral *priv)
+{
+       int ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+       if (priv->phy_clk.dev) {
+               ret = clk_disable(&priv->phy_clk);
+               if (ret)
+                       return ret;
+
+               ret = clk_free(&priv->phy_clk);
+               if (ret)
+                       return ret;
+       }
+#endif
+
+       ret = power_domain_off(&priv->phy_pd);
+       if (ret)
+               printf("conn_usb2_phy Power down failed! (error = %d)\n", ret);
+
+       return ret;
+}
+
+static int cdns3_generic_peripheral_clk_init(struct udevice *dev,
+                                            struct cdns3_generic_peripheral
+                                            *priv)
+{
+       int ret;
+
+       ret = clk_get_bulk(dev, &priv->clks);
+       if (ret)
+               return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+       ret = clk_enable_bulk(&priv->clks);
+       if (ret) {
+               clk_release_bulk(&priv->clks);
+               return ret;
+       }
+#endif
+
+       return 0;
+}
+
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+{
+       struct cdns3_generic_peripheral *priv = dev_get_priv(dev);
+       struct cdns3 *cdns3 = &priv->cdns3;
+
+       cdns3_role_irq_handler(cdns3);
+
+       return 0;
+}
+
+static int cdns3_generic_peripheral_probe(struct udevice *dev)
+{
+       int ret;
+       struct cdns3_generic_peripheral *priv = dev_get_priv(dev);
+       struct cdns3 *cdns3 = &priv->cdns3;
+
+       cdns3->dev = dev;
+
+       cdns3_setup_phy(dev, priv);
+       ret = cdns3_generic_peripheral_clk_init(dev, priv);
+       if (ret)
+               return ret;
+
+       ret = board_usb_init(dev->seq, USB_INIT_DEVICE);
+       ret = cdns3_init(cdns3);
+       printf("cdns3_uboot_initmode %d\n",  ret);
+
+       return 0;
+}
+
+static int cdns3_generic_peripheral_remove(struct udevice *dev)
+{
+       struct cdns3_generic_peripheral *priv = dev_get_priv(dev);
+       struct cdns3 *cdns3 = &priv->cdns3;
+       struct power_domain pd;
+       int ret = 0;
+
+       cdns3_exit(cdns3);
+
+       clk_release_bulk(&priv->clks);
+       cdns3_shutdown_phy(dev, priv);
+
+       if (!power_domain_get(dev, &pd)) {
+               ret = power_domain_off(&pd);
+               if (ret)
+                       printf("conn_usb2 power down failed!(error = %d)\n",
+                              ret);
+       }
+
+       return ret;
+}
+
+static int cdns3_generic_peripheral_ofdata_to_platdata(struct udevice *dev)
+{
+       struct cdns3_generic_peripheral *priv = dev_get_priv(dev);
+       struct cdns3 *cdns3 = &priv->cdns3;
+
+       cdns3->none_core_regs = (void __iomem *)devfdt_get_addr_index(dev, 0);
+       cdns3->xhci_regs = (void __iomem *)devfdt_get_addr_index(dev, 1);
+       cdns3->dev_regs = (void __iomem *)devfdt_get_addr_index(dev, 2);
+       cdns3->phy_regs = (void __iomem *)devfdt_get_addr_index(dev, 3);
+       cdns3->otg_regs = (void __iomem *)devfdt_get_addr_index(dev, 4);
+
+       return 0;
+}
+
+static const struct udevice_id cdns3_generic_peripheral_ids[] = {
+       { .compatible = "Cadence,usb3" },
+       {},
+};
+
+U_BOOT_DRIVER(cdns3_generic_peripheral) = {
+       .name   = "cdns3-generic-peripheral",
+       .id     = UCLASS_USB_GADGET_GENERIC,
+       .of_match = cdns3_generic_peripheral_ids,
+       .ofdata_to_platdata = cdns3_generic_peripheral_ofdata_to_platdata,
+       .probe = cdns3_generic_peripheral_probe,
+       .remove = cdns3_generic_peripheral_remove,
+       .priv_auto_alloc_size = sizeof(struct cdns3_generic_peripheral),
+};
+#endif
+
index 8495fec..890a35d 100644 (file)
@@ -279,6 +279,7 @@ static void cdns3_remove_roles(struct cdns3 *cdns)
        cdns3_gadget_remove(cdns);
 }
 
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
 int cdns3_uboot_init(struct cdns3_device *cdns3_dev)
 {
        struct device *dev = NULL;
@@ -372,3 +373,37 @@ void cdns3_uboot_handle_interrupt(int index)
                break;
        }
 }
+
+#else
+int cdns3_init(struct cdns3 *cdns)
+{
+       int ret;
+
+       ret = cdns3_core_init_role(cdns, USB_DR_MODE_PERIPHERAL);
+
+       cdns->role = cdns3_get_role(cdns);
+       dev_dbg(dev, "the init role is %d\n", cdns->role);
+       cdns3_set_role(cdns, cdns->role);
+       ret = cdns3_role_start(cdns, cdns->role);
+       if (ret) {
+               dev_err(dev, "can't start %s role\n", cdns3_role(cdns)->name);
+               goto err;
+       }
+
+       dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+
+       return 0;
+
+err:
+       cdns3_remove_roles(cdns);
+
+       return ret;
+}
+
+void cdns3_exit(struct cdns3 *cdns)
+{
+       cdns3_role_stop(cdns);
+       cdns3_remove_roles(cdns);
+       cdns3_reset_core(cdns);
+}
+#endif
index 3c93347..9309586 100644 (file)
@@ -56,7 +56,11 @@ struct cdns3_role_driver {
  * @wakeup_int: the wakeup interrupt
  */
 struct cdns3 {
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+       struct udevice *dev;
+#else
        struct device *dev;
+#endif
        void __iomem *xhci_regs;
        struct resource *xhci_res;
        struct usbss_dev_register_block_type __iomem *dev_regs;
@@ -66,8 +70,13 @@ struct cdns3 {
        int irq;
        struct cdns3_role_driver *roles[CDNS3_ROLE_END];
        enum cdns3_roles role;
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+       struct udevice *host_dev;
+       struct udevice *gadget_dev;
+#else
        struct device *host_dev;
        struct device *gadget_dev;
+#endif
        struct clk *cdns3_clks[CDNS3_NUM_OF_CLKS];
 
        int  index;
@@ -113,5 +122,9 @@ static inline void cdns3_role_irq_handler(struct cdns3 *cdns)
        cdns->roles[role]->irq(cdns);
 }
 
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+int cdns3_init(struct cdns3 *cdns);
+void cdns3_exit(struct cdns3 *cdns);
+#endif
 
 #endif /* __DRIVERS_USB_CDNS3_CORE_H */
index be5c54c..0a439c6 100644 (file)
 #include <linux/usb/gadget.h>
 #include <linux/usb/composite.h>
 
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
 #include "linux-compat.h"
+#endif
+
 #include "core.h"
 #include "gadget-export.h"
 #include "gadget.h"
@@ -2081,19 +2084,32 @@ static int usb_ss_init_ep0(struct usb_ss_dev *usb_ss)
        return 0;
 }
 
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
 static void cdns3_gadget_release(struct device *dev)
 {
        struct usb_ss_dev *usb_ss = container_of(dev, struct usb_ss_dev, dev);
 
        kfree(usb_ss);
 }
+#endif
 
 static int __cdns3_gadget_init(struct cdns3 *cdns)
 {
        struct usb_ss_dev *usb_ss;
        int ret;
-       struct device *dev;
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+       struct udevice *dev;
+       struct cdns3_generic_peripheral *priv = container_of(cdns,
+                               struct cdns3_generic_peripheral, cdns3);
 
+       usb_ss = &priv->usb_ss_dev;
+       dev = &usb_ss->dev;
+       dev->parent = cdns->dev;
+       dev_set_name(dev, "gadget-cdns3-dev");
+       cdns->gadget_dev = dev;
+       usb_ss->sysdev = cdns->dev;
+#else
+       struct device *dev;
        usb_ss = kzalloc(sizeof(*usb_ss), GFP_KERNEL);
        if (!usb_ss)
                return -ENOMEM;
@@ -2104,6 +2120,7 @@ static int __cdns3_gadget_init(struct cdns3 *cdns)
        dev_set_name(dev, "gadget-cdns3-dev");
        cdns->gadget_dev = dev;
        usb_ss->sysdev = cdns->dev;
+#endif
        ret = device_register(dev);
        if (ret)
                goto err1;
@@ -2158,7 +2175,8 @@ static int __cdns3_gadget_init(struct cdns3 *cdns)
        }
 
        /* add USB gadget device */
-       ret = usb_add_gadget_udc(&usb_ss->dev, &usb_ss->gadget);
+       ret = usb_add_gadget_udc((struct device *)(&usb_ss->dev),
+                                &usb_ss->gadget);
        if (ret < 0) {
                dev_err(dev, "Failed to register USB device controller\n");
                goto err4;
index fdb6e51..a15f16b 100644 (file)
 #include "cdns_misc.h"
 #endif
 
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+#include <dm.h>
+#include <clk.h>
+#include <power-domain.h>
+#endif
+
 #define gadget_to_usb_ss(g)  \
        (container_of(g, struct usb_ss_dev, gadget))
 
@@ -185,7 +191,11 @@ struct usb_ss_endpoint {
 };
 
 struct usb_ss_dev {
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+       struct udevice dev;
+#else
        struct device dev;
+#endif
        struct usbss_dev_register_block_type __iomem *regs;
 
        struct usb_gadget gadget;
@@ -211,11 +221,25 @@ struct usb_ss_dev {
        u32 usb_ien;
        u32 ep_ien;
        int setup_pending;
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+       struct udevice *sysdev;
+#else
        struct device *sysdev;
+#endif
        bool start_gadget; /* The device mode is enabled */
        struct list_head ep_match_list;
 };
 
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+struct cdns3_generic_peripheral {
+       struct cdns3 cdns3;
+       struct usb_ss_dev usb_ss_dev;
+       struct clk_bulk clks;
+       struct power_domain phy_pd;
+       struct clk phy_clk;
+};
+#endif
+
 #define OTG_STS_SELECTOR       0xF000          /* OTG status selector */
 
 #endif /* __DRIVERS_CDNS3_GADGET */